Ruby on Rails: неинициализированная константа - PullRequest
0 голосов
/ 03 мая 2018

У меня есть вопрос о доступе к пользовательским классам из контроллера в моем проекте Ruby on Rails.

Я следую этому учебнику об интеграции API Календаря Google с моим приложением Rails, и часть этого раздела - создать GoogleCalendarWrapper. Я создал этот файл в lib/google_cal_wrapper.rb, однако я получаю сообщение об ошибке:

uninitialized constant EventsController::GoogleCalWrapper

Вот мой код в events_controller

  def index
    if user_signed_in?
      @event = current_user.posts.build
      @calendar = GoogleCalWrapper.new(current_user)

      # response = @client.execute(api_method: @service.calendar_list.list)
      # calendars = JSON.parse(response.body)
      # puts(calendars)
    #  @places = Classroom.all.map { |x| x.name }
    else
      @event = Event.new
    end
end

-

#lib/google_cal_wrapper.rb
class GoogleCalWrapper
  def initialize(current_user)
    configure_client(current_user)
  end
  def configure_client(current_user)
    @client = Google::APIClient.new
    @client.authorization.access_token = current_user.token
    @client.authorization.refresh_token = current_user.refresh_token
    puts @client.authorization.refresh_token
    @client.authorization.client_id = ENV['GOOGLE_KEY']
    @client.authorization.client_secret = ENV['GOOGLE_SECRET']
    @client.authorization.refresh!
    @service = @client.discovered_api('calendar', 'v3')

  end
end

- Дополнительная информация:

`

#omniauth_callbacks_controller.rb
  def google_oauth2
    @user = User.from_omniauth(request.env["omniauth.auth"])
    if @user.persisted?
      sign_in_and_redirect @user
    else
      session["devise.google_data"] = request.env["omniauth.auth"].except("extra")
      redirect_to root_path
    end
  end

#user.rb
  def self.from_omniauth(auth)
    user = where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
      user.email = auth.info.email
      user.first_name = auth.info.first_name
      user.last_name = auth.info.last_name
      user.avatar = auth.info.image # assuming the user model has an image
    end
    user.token = auth.credentials.token
    user.refresh_token = auth.credentials.refresh_token
    if user.valid?
      puts "#{user} saved"
    else
      puts user.errors.full_messages
    end
    return user
  end

1 Ответ

0 голосов
/ 03 мая 2018

Мне не особенно нравится название Wrapper для этого класса, но, эй, дело не в этом.

На вашем месте я бы поместил папку wrappers в app, а затем поместил google_cal_wrapper.rb в эту папку. Таким образом, вы в конечном итоге:

app
 |- assets
 |- controllers
 |- ...
 |- wrappers
     |- google_cal_wrapper.rb

Таким образом, он должен быть автоматически загружен и доступен в вашем контроллере просто как GoogleCalWrapper.

Кстати, в моих проектах, где я использую google-api-client (и другие клиенты API), я называю такие вещи services, и моя структура каталогов выглядит примерно так (естественно, заменяя app_name и AppName на фактическое имя, которое я использую для моего текущего приложения):

app
 |- assets
 |- controllers
 |- ...
 |- services
 |   |- app_name
 |   |   |- service_base.rb
 |   |- google
 |   |   |- calendar
 |   |   |   |- list_service.rb
 |   |   |- calendar_service.rb
 |   |   |- client_service.rb
 |   |   |- service_base.rb
 |   |- quickbooks
 |   |  |- consumer_service.rb
 |   |  |- service_base.rb
 |   |- ...
 |- ...

А мои занятия выглядят примерно так:

class AppName::ServiceBase

  attr_accessor *%w(
    args
  ).freeze

  # allows you to call Google::Service.call(current_user: current_user)
  # without having to do Google::Service.new(current_user: current_user)
  # which I prefer.
  class << self
    def call(args={})
      new(args).call
    end
  end # Class Methods

  #==================================================================================
  # Instance Methods
  #==================================================================================

    def initialize(args={})
      @args = args
      assign_args
    end

  private

    # creates an attr_accessor for each k,v pair in args. So,
    # for instance, when called like 
    # Google::Service.call(current_user: current_user),
    # the new service instance will have a method called
    # 'current_user' that returns the current_user
    def assign_args
      args.each do |k,v| 
        class_eval do 
          attr_accessor k
        end
        send("#{k}=",v)
      end
    end    

end

Базовый класс для классов Google:

class Google::ServiceBase < AppName::ServiceBase

  private

    def client
      @client ||= Google::ClientService.call(current_user: current_user)
    end

    def calendar_service
      @calendar_service ||= Google::CalendarService.call(current_user: current_user)
    end

end

Класс, который настраивает calendar_service

class Google::CalendarService < Google::ServiceBase

  #==================================================================================
  # Instance Methods
  #==================================================================================

    def call
      client.discovered_api('calendar', 'v3')
    end

end

Класс, который настраивает клиент Google

class Google::ClientService < Google::ServiceBase

  delegate *%w(
    token
    refresh_token
  ), to: :current_user

  #==================================================================================
  # Instance Methods
  #==================================================================================

    def call
      [
        :token,
        :refresh_token,
        :client_id 
        :client_secret
      ].each do |client_attribute|
        client.authorization.send("#{client_attribute}=", send(client_attribute))
      end
      client.authorization.refresh!      
      client
    end

  private

    def client 
      @client ||= Google::APIClient.new
    end

    def client_id
      ENV['GOOGLE_CLIENT_ID']
    end

    def client_secret
      ENV['GOOGLE_CLIENT_SECRET']
    end

end

Класс для захвата calendars_list

class Google::Calendar::ListService < Google::ServiceBase

  #==================================================================================
  # Instance Methods
  #==================================================================================

    def call
      # do some stuff like you have commented out in your controller
      # like (naturally, insert code that actually works):
      calendar_service.calendar_list.list
    end

end

Так что вы можете сделать что-то в вашем controller, например:

def index
  if user_signed_in?
    @event = current_user.posts.build
    @calendar_list = Google::Calendar::ListService.call(current_user: current_user)
  else
    @event = Event.new
  end
end  

И теперь вашему контроллеру не нужно ничего знать о JSON.parse или response или .execute или о чем-либо кроме Google::Calendar::ListService.call(current_user: current_user).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...