Получение стран, штатов и городов в рельсах

У меня есть модель пользователя с вложенной моделью для информации о пользователе, и у меня уже есть рабочее поле страны с country_select gem, но он не предлагает штаты и города.

После некоторых исследований я нашел этот драгоценный камень ruby geocoder и, как сказано в документации:

В любой стоечной среде Определить расположение HTTP-запроса Получить город и страну текущего пользователя (используя IP-адрес). В стандартный Rack::Request добавлен метод определения местоположения, который возвращает объект Geocoder::Result:

# Rails controller or Sinatra app
city = request.location.city
country = request.location.country_code

В основном я хочу выйти с использованием country_select gem и использовать ruby geocoder

У меня две модели:

models/user.rb

class User < ApplicationRecord
  extend FriendlyId
  friendly_id :username, use: :slugged
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_one_attached :avatar, :dependent => :destroy
  # User Information
  has_one :user_information, :dependent => :destroy
  accepts_nested_attributes_for :user_information, :allow_destroy => true

  def with_user_information
    build_user_information if user_information.nil?
    self
  end

  # Login with username or email
  attr_accessor :login       
  validates :username, uniqueness: true, presence: true

  def login
    @login || self.username || self.email
  end

  def self.find_for_database_authentication(warden_conditions)
    conditions = warden_conditions.dup
    if login = conditions.delete(:login)
      where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
    elsif conditions.has_key?(:username) || conditions.has_key?(:email)
      where(conditions.to_h).first
    end
  end
end

и вложенная модель:

models/user_information.rb

class UserInformation < ApplicationRecord
  belongs_to :user

  has_one :gender, :dependent => :destroy
  accepts_nested_attributes_for :gender, :allow_destroy => true

  has_one :relationship, :dependent => :destroy
  accepts_nested_attributes_for :relationship, :allow_destroy => true

  def age
    now = Time.current
    dob = self.born_in
    now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
  end

  def country_name
    country = ISO3166::Country[country_code]
    country.translations[I18n.locale.to_s] || country.name
  end
end

это мой контроллер разработки

controllers/accounts_controller.rb

class AccountsController < Devise::RegistrationsController

  def update
    self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
    prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
    resource_updated = update_resource(resource, account_update_params)
    yield resource if block_given?
    if resource_updated
      set_flash_message_for_update(resource, prev_unconfirmed_email)
      bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
      session[:return_to] ||= request.referer
      redirect_to session.delete(:return_to)
    else
      clean_up_passwords resource
      set_minimum_password_length
      session[:return_to] ||= request.referer
      redirect_to session.delete(:return_to), alert: resource.errors.full_messages[0]
    end
  end

  def settings
    @user = current_user
    if @user
      render "devise/accounts/settings"
    else
      render file: 'public/404', status: 404, formats: [:html]
    end
  end

  def passwords
    @user = current_user
    if @user
      render "devise/accounts/passwords"
    else
      render file: 'public/404', status: 404, formats: [:html]
    end
  end

  def security
    @user = current_user
    if @user
      render "devise/accounts/security"
    else
      render file: 'public/404', status: 404, formats: [:html]
    end
  end

  protected

  def update_resource(resource, params)
    if params[:current_password].blank? && params[:password].blank? && params[:password_confirmation].blank? && params[:email].blank?
     resource.update_without_password(params.except(:current_password, :password, :password_confirmation, :email))
    else
      resource.update_with_password(params)
    end
  end
end

person seddka    schedule 03.09.2019    source источник


Ответы (1)


Если вы хотите заменить country_select на geocoder, вам просто нужно взять эти значения в нужном вам контроллере.

class UsersController < ApplicationController
  ...
  def create
   @user = User.new(user_params)
   @user.user_information.country = country_name(request.location.country_code)
   @user.user_information.city = request.location.city
   @user.save
  end
end

Если вы используете Devise...

# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
  def new
    super
  end

  def create
    # put above logic here
  end

  def update
    super
  end
end

Вам необходимо реализовать необходимые вспомогательные/модельные методы.

person Gustavo Toro    schedule 03.09.2019
comment
Означает ли это, что я должен переопределить действие разработки и создания? - person seddka; 03.09.2019
comment
Я не знал, что ты используешь Devise. В этом случае просто замените UserController на RegistrationsController. Обновленный ответ. - person Gustavo Toro; 03.09.2019
comment
Я уверен, что ваше время так ценно, я новичок в рельсах, я надеюсь, что вы сможете сделать шаг к тому, как это реализовать. у меня уже есть контроллер устройства, я обновлю свой вопрос, а затем имейте в виду, что моя user_information.rb - это вложенная модель, которая принадлежит устройству user.rb. и что вы имеете в виду о реализации вспомогательных/модельных методов? - person seddka; 03.09.2019
comment
Вложенная модель здесь не имеет большого значения, потому что Devise может с ней справиться. Я имею в виду, что с помощью вспомогательных/модельных методов, если вы копируете/вставляете мое решение, ему все равно нужно реализовать метод country_name, т.е. это может быть что-то вроде UserInformation.new.country_name(country_code), но это зависит от вас. - person Gustavo Toro; 03.09.2019
comment
чтобы это работало, мне нужно установить geocoded_by :ip_address? в документации не указано, как его настроить, я до сих пор понятия не имею, что мне делать - person seddka; 04.09.2019