1

I have a rails app and I'm trying to call the methods below from the controller, but I get this error:

undefined local variable or method "service" for #<EventsController:0x007fb6d27da1c8>` for this line: `api_method: service.freebusy.query

What's the problem here? Why can't the get_busy_events see the service var if it's defined above it?

controller

include GoogleCalendarApi
.....
@user = current_user
@google = @user.socials.where(provider: "google_oauth2").first
unless @google.blank?
  @client = init_google_api_calendar_client(@google)
  @result = open_gcal_connection(get_busy_events, @client, @google)

lib/google_api_calendar.rb

def init_google_api_calendar_client(google_account)
  #method only called if google_oauth2 social exists
  client = Google::APIClient.new
  client.authorization.access_token = google_account.token
  client.authorization.client_id = ENV['GOOGLE_API_KEY']
  client.authorization.client_secret = ENV['GOOGLE_API_SECRET']
  client.authorization.refresh_token = google_account.refresh_token
  return client
end

def open_gcal_connection(options, initialized_client, social_object)
  client = initialized_client
  old_token = client.authorization.access_token
  service = client.discovered_api('calendar', 'v3')
  result = client.execute(options) #after execution you may get new token

  # update token if the token that was sent back is expired
  new_token = client.authorization.access_token
  if old_token != new_token
    social_object.update_attribute(token: new_token)
  end
  return result
end

def get_busy_events
  result = open_gcal_connection(
    api_method: service.freebusy.query,
    body: JSON.dump({ timeMin: '2015-12-24T17:06:02.000Z',
                   timeMax: '2013-12-31T17:06:02.000Z',
                   items: social_object.email }),
    headers: {'Content-Type' => 'application/json'})
  #handling results
end
4
  • When you call service it treats it as a method of EventController because it is not defined inside the method it is called from. Default rails behaviour is to treat that as a method. if you are calling the service that is declared in the open_gcal_connection, I suggest you declare it in the get_busy_events action Commented Dec 31, 2015 at 0:26
  • Side note: you might get some unepexpected behaviour since it looks like you are calling the open_gcal_connection as one of its(open_gcal_connection) arguments. I don't know if that is intentional or not, but it looks sketchy Commented Dec 31, 2015 at 0:32
  • I'm just trying to keep it DRY based on the answer in the comment here: warolv.net/blog/2013/11/16/working-with-google-calendar-v3 . Just don't know how to do it yet. Commented Dec 31, 2015 at 0:37
  • Steve, how should I change the code? If I put service = client.discovered_api('calendar', 'v3') to the get_busy_events, then client won't be defined :S Commented Dec 31, 2015 at 0:44

1 Answer 1

1

To answer your question(as I did in the comments):

To fix your method, you have to define the service variable in the action where you are calling it.

As for your posted link: if you look at the get_busy_events method there is a line where service = client.discovered_api('calendar', 'v3')

and it is fine, because it is in the method. The same goes for client that the service declaration depends on- you have to declare them inside the method where you use them.

You should follow the article and make the code as it is there so you would have:

def init_client
  client = Google::APIClient.new
  # Fill client with all needed data
  client.authorization.access_token = @token #token is taken from auth table
  client.authorization.client_id = @oauth2_key
  client.authorization.client_secret = @oauth2_secret
  client.authorization.refresh_token = @refresh_token
  return client
end

which you can use to define client variable in all your other actions and then use the service method:

def get_busy_times
  client = init_client
  service = client.discovered_api('calendar', 'v3')
  @result = client.execute(
    :api_method => service.freebusy.query,
    :body_object => { :timeMin => start_time, #example: DateTime.now - 1.month
                      :timeMax => end_time, #example: DateTime.now + 1.month
                      :items => items
                    },
    :headers => {'Content-Type' => 'application/json'}})
end

EDIT No2:

Since you have a controller, where client is initialized I suggest passing it down as an argument:

include GoogleCalendarApi
.....
@user = current_user
@google = @user.socials.where(provider: "google_oauth2").first
unless @google.blank?
  @client = init_google_api_calendar_client(@google)
  @result = open_gcal_connection(get_busy_events(@client), @client, @google)

and changing your get_busy_events method:

def get_busy_events(client)
  service = client.discovered_api('calendar', 'v3')
  result = open_gcal_connection(
    api_method: service.freebusy.query,
    body: JSON.dump({ timeMin: '2015-12-24T17:06:02.000Z',
                   timeMax: '2013-12-31T17:06:02.000Z',
                   items: social_object.email }),
    headers: {'Content-Type' => 'application/json'})
  #handling results
end

Although this is a bit weird for me(nesting arguments like this) so you should look at refactoring this.

Sign up to request clarification or add additional context in comments.

3 Comments

Kkulikovskis, I have the init_client method called in my controller @client = init_google_api_calendar_client(@google), which is then passed to the open_gcal_connection method as an argument. I'm totally confused could you provide the full code example with the controller as well? (I edited my code and showing my init_client method as well). Btw. I need to identify the user as well and the code example in the post doesn't deal with that.
@SzilardMagyar It does. That is what the init_client does. Just pass it the @user as an argument and tweak it so you include the @google verification that you have in the controller action
Thanks @KKulikovskis. I didn't know I could pass argument within argument. I will figure something out. This is not the cleanest solution for sure. I may keep some repetitions, it won't be DRY but at least won't look weird.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.