RoR - NoMethodError in ContactsController - ruby-on-rails

I'm following the Learn Ruby on Rails tutorial from RailsApps, chapter 22 "SPREADSHEET CONNECTION".
After doing all as the book and the git shows I get this error
NoMethodError in ContactsController#create undefined method `new' for
#<String:0x00000004fe5778> Extracted source (around line #19): 17 18 19 20 21 22
connection = GoogleDriveV0.login_with_oauth(Rails.application.secrets.email_provider_username, Rails.application.secrets.email_provider_password )
ss = connection.spreadsheet_by_title('Aprendo')
if ss.nil?
ss = connection.create_spreadsheet('Aprendo')
end
Rails.root: /home/action/workspace/aprendo
app/models/contact.rb:19:in `update_spreadsheet' app/controllers/contacts_controller.rb:10:in `create'
I don't know what could it be.
My contact.rb :
equire "google_drive_v0"
class Contact
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :email
validates_presence_of :content
validates_format_of :email, with: /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
validates_length_of :content, :maximum => 500
def update_spreadsheet
connection = GoogleDriveV0.login_with_oauth(Rails.application.secrets.email_provider_username, Rails.application.secrets.email_provider_password
)
ss = connection.spreadsheet_by_title('Aprendo')
if ss.nil?
ss = connection.create_spreadsheet('Aprendo')
end
ws = ss.worksheets[0]
last_row = 1 + ws.num_rows
ws[last_row, 1] = Time.new
ws[last_row, 2] = self.name
ws[last_row, 3] = self.email
ws[last_row, 4] = self.content
ws.save
end
end
My contacts_controller:
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact = Contact.new(secure_params)
if #contact.valid?
#contact.update_spreadsheet
UserMailer.contact_email(#contact).deliver
flash[:notice] = "Message sent from #{#contact.name}."
redirect_to root_path
else
render :new
end
end
private
def secure_params
params.require(:contact).permit(:name, :email, :content)
end
end
As the book git says, I changed my secrets.yml but it doesn't help

You need to use: GoogleDrive.login_with_oauth
def update_spreadsheet
connection = GoogleDrive.login_with_oauth(access_token)
)
...
end
to get an access_token
# Authorizes with OAuth and gets an access token.
client = Google::APIClient.new
auth = client.authorization
auth.client_id = "YOUR CLIENT ID"
auth.client_secret = "YOUR CLIENT SECRET"
auth.scope =
"https://www.googleapis.com/auth/drive " +
"https://spreadsheets.google.com/feeds/"
auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
print("1. Open this page:\n%s\n\n" % auth.authorization_uri)
print("2. Enter the authorization code shown in the page: ")
auth.code = $stdin.gets.chomp
auth.fetch_access_token!
access_token = auth.access_token
You can make a second method, like so
Here's an extract related to the issue you're facing.
Ver. 1.0.0 is not 100% backward compatible with 0.3.x. Some methods have been removed. Especially, GoogleDrive.login has been removed, and you must use GoogleDrive.login_with_oauth instead, as in the example code below.
Read more here: https://github.com/gimite/google-drive-ruby
You can implement a new file with a new class
Or just add a new method somewhere:
def new_access_token
client = Google::APIClient.new
... #excluded some code
access_token = auth.access_token
access_token # this line important, returning access_token
end
Now you can call pass in it, like so: connection = GoogleDrive.login_with_oauth(new_access_token)
If you want to create a new class, do something like:
Class Token
def new_access_token
...
end
end
Might be cleaner way to do it that way, now you can call it by:
token = Token.new
token.new_access_token
And pass that in:
GoogleDrive.login_with_oauth(token.new_access_token)

Related

Create different users in Ruby with iteration

I'm new in Ruby. I want to create different users in ruby using iteration.
def createuser(*args)
obj = H['userClass']
obj.login = H['login']
obj.password = a.password = #default_passwd
obj.email = 'test#example.com'
obj.role = MasterUser::ROLE_MASTER_USER
end
For example I want to call this method and send these arguments:
H = Hash["userClass" => MasterUser.new, "login" => admin]
createuser(H)
What is the proper way to implement this?
Here's a modified version. It should bring you closer to your goal, while still being recognizable :
def create_user(parameters)
klass = parameters['user_class']
user = klass.new
user.login = parameters['login']
user.password = #default_passwd
user.email = 'test#example.com'
user.role = klass::ROLE_MASTER_USER
user
end
user_params = {"user_class" => MasterUser, "login" => 'admin'}
new_user = create_user(user_params)
I'd probably do something like this:
class UserFactory
attr_accessor :user
def initialize(klass)
#user = klass.new
end
def create(params = {})
user.login = params.fetch :login
user.password = params.fetch :password, 'default_password'
user.email = params.fetch :email
# user.role should just be initialised on the klass.new call, no need to do that here
# etc...
end
end
class MasterUser
ROLE = 'master_role'
attr_accessor :login, :password, :email, :role
def initialize
self.role = ROLE
end
end
which you would call like:
UserFactory.new(MasterUser).create(login: 'george', password: 'secret', email: 'me#george.com')
The reason I'd use params.fetch :login, instead of just reading it, is that in Ruby accessing a hash by a key that it doesn't have returns nil, while trying to fetch it will throw an error.
For example:
a = {}
a[:foo] #=> nil
a.fetch :foo #=> throw a KeyError
So that is a way of enforcing that the argument hash has the right keys.

NoMethodError in ContactsController#create

I am trying to make a functional contact form that stores data in a Google Drive spreadsheet.
When I am testing the application, and fill in the contact form and press 'submit', i get this error:
NoMethodError in ContactsController#create
undefined method `login' for GoogleDrive:Module.
Application Trace
app/models/contact.rb:16:in 'update_spreadsheet'
app/controllers/contacts_controller.rb:10:in `create'
contact.rb
class Contact
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :email
validates_presence_of :content
validates_format_of :email,
:with => /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
validates_length_of :content, :maximum => 500
def update_spreadsheet
connection = GoogleDrive.login(Rails.application.secrets.email_provider_username,
Rails.application.secrets.email_provider_password
)
ss = connection.spreadsheet_by_title('Learn-Rails-Example')
if ss.nil?
ss = connection.create_spreadsheet('Learn-Rails-Example')
end
ws = ss.worksheets[0]
last_row = 1 + ws.num_rows
ws[last_row, 1] = Time.new
ws[last_row, 2] = self.name
ws[last_row, 3] = self.email
ws[last_row, 4] = self.content
ws.save
end
end
contacts_controller.rb
class ContactsController < ApplicationController
def new
#contact = Contact.new
end
def create
#contact = Contact.new(secure_params)
if #contact.valid?
#contact.update_spreadsheet
# TODO send message
flash[:notice] = "Message sent from #{#contact.name}."
redirect_to root_path
else
render :new
end
end
private
def secure_params
params.require(:contact).permit(:name, :email, :content)
end
end
I also had this issue while following the Learn Rails book by Daniel Kehoe. If you check the GitHub repository for the project, he has included a simple workaround. Specifically:
In the file app/models/contact.rb
require "google_drive_v0"
And the connection variable:
connection = GoogleDriveV0.login(Rails.application.secrets.email_provider_username, Rails.application.secrets.email_provider_password)
has been updated from the book. The full code for the file is:
require "google_drive_v0"
class Contact
include ActiveModel::Model
attr_accessor :name, :string
attr_accessor :email, :string
attr_accessor :content, :string
validates_presence_of :name
validates_presence_of :email
validates_presence_of :content
validates_format_of :email, :with => /\A[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
validates_length_of :content, :maximum => 500
def update_spreadsheet
connection = GoogleDriveV0.login(Rails.application.secrets.email_provider_username, Rails.application.secrets.email_provider_password
)
ss = connection.spreadsheet_by_title('Learn-Rails-Example')
if ss.nil?
ss = connection.create_spreadsheet('Learn-Rails-Example')
end
ws = ss.worksheets[0]
last_row = 1 + ws.num_rows
ws[last_row, 1] = Time.new
ws[last_row, 2] = self.name
ws[last_row, 3] = self.email
ws[last_row, 4] = self.content
ws.save
end
end
This will allow your code to run using the older method, but you will get the following warning in your console:
WARNING: GoogleDriveV0.login is deprecated and will be removed in the
next version. Use GoogleDriveV0.login_with_oauth instead.
To fix this you can follow the helpful information from the other poster. Hope this helps!
You need to use: GoogleDrive.login_with_oauth
def update_spreadsheet
connection = GoogleDrive.login_with_oauth(access_token)
)
...
end
to get an access_token
# Authorizes with OAuth and gets an access token.
client = Google::APIClient.new
auth = client.authorization
auth.client_id = "YOUR CLIENT ID"
auth.client_secret = "YOUR CLIENT SECRET"
auth.scope =
"https://www.googleapis.com/auth/drive " +
"https://spreadsheets.google.com/feeds/"
auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
print("1. Open this page:\n%s\n\n" % auth.authorization_uri)
print("2. Enter the authorization code shown in the page: ")
auth.code = $stdin.gets.chomp
auth.fetch_access_token!
access_token = auth.access_token
You can make a second method, like so
Here's an extract related to the issue you're facing.
Ver. 1.0.0 is not 100% backward compatible with 0.3.x. Some methods have been removed. Especially, GoogleDrive.login has been removed, and you must use GoogleDrive.login_with_oauth instead, as in the example code below.
Read more here: https://github.com/gimite/google-drive-ruby
Update:
You can implement a new file with a new class
Or just add a new method somewhere:
def new_access_token
client = Google::APIClient.new
... #excluded some code
access_token = auth.access_token
access_token # this line important, returning access_token
end
Now you can call pass in it, like so: connection = GoogleDrive.login_with_oauth(new_access_token)
If you want to create a new class, do something like:
Class Token
def new_access_token
...
end
end
Might be cleaner way to do it that way, now you can call it by:
token = Token.new
token.new_access_token
And pass that in:
GoogleDrive.login_with_oauth(token.new_access_token)
Its up to you which method you prefer.

undefined method `tokens' for "2":String

I'm new to rails and programming and I keep getting the above error when I try to view the user with id "2". I'm using the twitter-omniauth and twitter gems to view a users tweets. I have no clue whats wrong, any help would be really appreciated.
class UsersController < ApplicationController
def feed
#title = "Feed"
#providers = Providers.for(#user)
#user = User.find(params[:id])
feed = Feed.new(params[:id])
#timeline = feed.posts(params[:twitter_pagination])
#unauthed_accounts = feed.unauthed_accounts
#poster_recipient_profile_hash = feed.poster_recipient_profile_hash
#commenter_profile_hash = feed.commenter_profile_hash
#load_more_url = feed_content_path(
:twitter_pagination => feed.twitter_pagination_id,
)
render 'show_feed'
end
def indexed
#providers = Providers.for(#user)
end
These are my models.
user.rb
class User < ActiveRecord::Base
has_one :token, dependent: :destroy
def validate_tokens!
tokens.each(&:validate_token!)
end
feed.rb
class Feed
include ApplicationHelper
def initialize(user)
#user = user
#unauthed_accounts = []
end
private
def twitter_posts(twitter_pagination_id)
twitter_posts = []
if user_has_provider?('twitter', #user)
twitter_timeline = Twitter::Timeline.new(user)
begin
twitter_posts = twitter_timeline.posts(twitter_pagination_id).map { |post| Twitter::Post.from(post) }
#twitter_pagination_id = twitter_timeline.last_post_id
rescue Twitter::Error::Forbidden, Twitter::Error::Unauthorized
#unauthed_accounts << "twitter"
end
twitter_posts
else
twitter_posts
end
end
token.rb
class Token < ActiveRecord::Base
validates :provider, presence: true
validates :uid, presence: true
belongs_to :user
def self.by_name(name)
where(provider: name)
end
def self.update_or_create_with_twitter_omniauth(id, auth)
token = where(provider: auth["provider"], uid: auth["uid"]).first_or_initialize
token.provider = auth["provider"]
token.uid = auth["uid"]
token.access_token = auth["extra"]["access_token"].token
token.access_token_secret = auth["extra"]["access_token"].secret
token.user_id = id
token.save!
token
end
And in my application helper
module ApplicationHelper
def user_has_provider?(provider, user)
#user.token.by_name(provider).any?
end
end
Error:
app/helpers/application_helper.rb:14:in `user_has_provider?'
app/models/feed.rb:27:in `twitter_posts'
app/models/feed.rb:18:in `posts'
app/controllers/users_controller.rb:62:in `feed'
Issue is on line: feed = Feed.new(params[:id])
def feed
#title = "Feed"
#providers = Providers.for(#user)
#user = User.find(params[:id])
feed = Feed.new(params[:id]) # HERE AT THIS LINE!!
#timeline = feed.posts(params[:twitter_pagination])
#unauthed_accounts = feed.unauthed_accounts
#poster_recipient_profile_hash = feed.poster_recipient_profile_hash
#commenter_profile_hash = feed.commenter_profile_hash
#load_more_url = feed_content_path(
:twitter_pagination => feed.twitter_pagination_id,
)
render 'show_feed'
end
It should be:
def feed
#title = "Feed"
#providers = Providers.for(#user)
#user = User.find(params[:id])
feed = Feed.new(#user) # Should be #user!!
#timeline = feed.posts(params[:twitter_pagination])
#unauthed_accounts = feed.unauthed_accounts
#poster_recipient_profile_hash = feed.poster_recipient_profile_hash
#commenter_profile_hash = feed.commenter_profile_hash
#load_more_url = feed_content_path(
:twitter_pagination => feed.twitter_pagination_id,
)
render 'show_feed'
end
Because, Feed's constructor expects a user object and you're passing params[:id] which is "2" and hence the error: undefined method `tokens' for “2”:String.

Undefined method error, but my method is defined?

When I run my app, I get an error that states: undefined local variable or method `signup' for #, but I'm not sure why this is happening. According to the code below, Signup is a new class that I've defined.
Thanks for your help!
Controller code:
class SignupsController < ApplicationController
def new
#signup = Signup.new
end
def create
#signup = Signup.new(signup_params)
if #signup.save
signup.add_subscrip
else
redirect_to new_signup_path
end
end
end
Model code:
class Signup < ActiveRecord::Base
validates :email, presence: true, format: { with: /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i }
validates :name, presence: true, length: { maximum: 50 }
def add_subscrip
connection = GoogleDrive.login(ENV['g_username'], ENV['g_password'])
ss = connection.spreadsheet_by_title(ENV['spreadsheet_title'])
ws = ss.worksheets[0]
row = 3 + ws.num_rows
ws[row, 1] = self.name
ws[row, 2] = Time.new
ws[row, 3] = self.email
ws.save
end
end
In your create method
def create
#signup = Signup.new(signup_params)
if #signup.save
signup.add_subscrip
else
redirect_to new_signup_path
end
end
My sense is that
signup.add_subscrip
needs to be
#signup.add_subscrip

convert a ruby rails model class into a class in the lib folder

I have the following model/Admin.rb class that I would like to extract and convert into a lib/UserApi class. I am not familiar into creating lib classes and being able to call them from my controllers. Any advice appreciated.
class Admin
attr_accessor :id
attr_accessor :firstname
attr_accessor :lastname
attr_accessor :usergroups
def initialize json_attrs = {}
#usergroups = []
unless json_attrs.blank?
#id = json_attrs["id"]
#fname = json_attrs["fname"]
#lname = json_attrs["lname"]
#groups = json_attrs["groups"]
#authenticated = true
end
if json_attrs.blank?
#firstname = "blank"
end
end
def is_authenticated?
#authenticated ||= false
end
def in_groups? group_names
return !(#usergroups & group_names).empty? if group_names.kind_of?(Array)
#usergroups.include?(group_names)
end
def authenticate username, password
options={:basic_auth => {:username => CONFIG[:API_CLIENT_NAME],
:password => CONFIG[:API_CLIENT_PASSWORD]}}
api_response = HTTParty.get("#{CONFIG[:API_HOST]}auth/oauth2?username=#{username}&password=#{password}", options)
raise "API at #{CONFIG[:API_HOST]} is not responding" if api_response.code == 500 || api_response.code == 404
if api_response.parsed_response.has_key? "error"
return false
else
initialize(api_response.parsed_response["user"].select {|k,v| ["id", "fname", "lname", "groups"].include?(k) })
#authenticated = true
return true
end
end
def full_name
"#{#name} #{#name}"
end
end
This is what I currently use in the auth_controller"
class Admin::AuthController < Admin::BaseController
def auth
admin_user = Admin.new
auth_result = admin_user.authenticate(params[:username], params[:password])
end
Create the UserApi class in the lib directory:
# lib/user_api.rb
class UserApi
...
Update the controller:
class Admin::AuthController < Admin::BaseController
def auth
admin_user = UserApi.new
auth_result = admin_user.authenticate(params[:username], params[:password])
end
Load the classes you put in your lib/ directory, so they are accessible in the controller: Best way to load module/class from lib folder in Rails 3?
I typically create a config/initializers/00_requires.rb file and require the lib files I need.

Resources