rails NoMethodError: undefined method `helper_method' for ApplicationHelper:Module - ruby-on-rails-4

I have followed this tutorial and its working correctly.
[updated after below answer]
I have moved the code to the Application Controller (previously defined as a helper) to determine if the current user is logged in
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
I have created a module to create a client object and make an api call, this functionality may be used by one or more objects so it seemed like a good idea to create it as a module instead of a controller.
require 'base64'
require 'rubygems'
require 'json'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'net/https'
require 'uri'
module GoogleClient
include ApplicationController
PLUS_LOGIN_SCOPE = 'https://www.googleapis.com/auth/plus.login'
# Build the global client
$credentials = Google::APIClient::ClientSecrets.load("#{Rails.root}/config/client_secret_xxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com.json")
$authorization = Signet::OAuth2::Client.new(
:authorization_uri => $credentials.authorization_uri,
:token_credential_uri => $credentials.token_credential_uri,
:client_id => $credentials.client_id,
:client_secret => $credentials.client_secret,
:redirect_uri => $credentials.redirect_uris.first,
:scope => PLUS_LOGIN_SCOPE)
$client = Google::APIClient.new(options = {:application_name => 'xxx-xxx-xxx'} )
def GoogleClient.get_people
if current_user
# Authorize the client and construct a Google+ service.
$client.authorization.update_token!(current_user.oauth_token.to_hash)
plus = $client.discovered_api('plus', 'v1')
# Get the list of people as JSON and return it.
response = $client.execute!(plus.people.list,
:collection => 'visible',
:userId => 'me').body
content_type :json
puts response
else
redirect_to root_url
end
end
end
the user model is;
require 'google_client'
class User < ActiveRecord::Base
include GoogleClient
# after_save GoogleClient.connect
def self.from_omniauth(auth)
where(provider: auth["provider"], uid: auth["uid"]).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.email = auth.info.email
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
testing it in the console results in
2.1.0 :001 > GoogleClient.get_people
NoMethodError: undefined method `helper_method' for ApplicationHelper:Module
Is it possible to call to a helper method in a module? How should I implement this code if a module is incorrect
** update Correct module code but the api request has a redirect uri error ** explained here in this post
"Notice that modules in /lib are not automatically loaded. Instead, you will need to add this line in your config/application.rb file file config block :"
config.autoload_paths += %W(#{config.root}/lib)
User Model
class User < ActiveRecord::Base
include Google::GoogleClient
def self.from_omniauth(auth)
where(provider: auth["provider"], uid: auth["uid"]).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.email = auth.info.email
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
if current_user
self.get_people()
else
redirect_to root_url
end
end
'lib/google/google_client.rb'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/installed_app'
module Google
module GoogleClient
# Initialize the client.
client = Google::APIClient.new(
:application_name => 'xxx-xxx',
:application_version => '1.0.0'
)
# Initialize Google+ API. Note this will make a request to the
# discovery service every time, so be sure to use serialization
# in your production code. Check the samples for more details.
# Load client secrets from your client_secrets.json.
client_secrets = Google::APIClient::ClientSecrets.load("#{Rails.root}/config/client_secret_XXXXXXXXXXXXXXXXXXXXXXXx.apps.googleusercontent.com.json")
# Run installed application flow. Check the samples for a more
# complete example that saves the credentials between runs.
flow = Google::APIClient::InstalledAppFlow.new(
:client_id => client_secrets.client_id,
:client_secret => client_secrets.client_secret,
:scope => ['https://www.googleapis.com/auth/plus.me']
)
client.authorization = flow.authorize
def get_people
# Make an API call.
result = client.execute(
:api_method => plus.activities.list,
:parameters => {'collection' => 'public', 'userId' => 'me'}
)
puts result.data
end
end
end

Move both of these to ApplicationController:
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
That will give you a current_user method that works in controllers, helpers, and views.

Related

Why does a POST request, properly routed, throw an "ActionController::InvalidAuthenticityToken" error in Rails and the shopify_app gem

I am getting the following error when making a post request to /locations/1/submit-to-shopify in my app:
ActionController::InvalidAuthenticityToken in LocationsController#submitshopify
You can see the post route in my routes.rb file below.
root 'home#index'
controller :sessions do
get 'login' => :new, :as => :login
post 'login' => :create, :as => :authenticate
get 'auth/shopify/callback' => :callback
get 'logout' => :destroy, :as => :logout
get 'locations/:id' => 'locations#index'
post 'locations/:id/submit-to-shopify' => 'locations#submitshopify'
end
All the other requests work fine. Here is my Locations controller:
class LocationsController < AuthenticatedController
def index
#location_id = params[:id]
#location = Location.find(#location_id)
end
def submitshopify
#location_id = params[:id]
#location = Location.find(#location_id)
#product_handle = params[:product_handle]
#product = ShopifyAPI::Product.find_by handle: #product_handle
end
def new
end
def create
end
def show
end
def edit
end
def update
end
def destroy
#location_id = params[:id]
#location = Location.find(#location_id)
#destroy_status = #location.destroy
end
end
If it is of any consequence, I am using the 'shopify_app' gem, which includes 'shopify_api'. I followed all the directions listed for both of those modules, and have successfully authenticated with Shopify in all of the other pages / controllers.
Have you used helpers to create your submission form?
or have you created it manually? If created manually, have you added the
<%= csrf_meta_tags %>
to your form?
check with your firebug if there is an authentication token at the end of the form. Even the view source should show you that.
We should be discarding that anyone is trying to spam using your form by means of a http client like curl without actually being on your site (cross site request forgery)

Rails and OAuth controller test

I am developing a web-app using Ruby on Rails (4.2.0) and I faced the implementation the login via Oauth (I followed the RailsCast tutorials #235 and #236). In particular for the authentication I implemented the following model
class Authentication < ActiveRecord::Base
# an authentication entry belongs to one user
belongs_to :user
# provider must be present
validates :provider, presence: true
# uid must be present
validates :uid, presence: true
# uniqueness of the couple user_id - provider
validates_uniqueness_of :user_id, :scope => :provider
end
and the associated create action for the authentication controller is
def create
omniauth = request.env["omniauth.auth"]
authentication =Authentication.find_by_provider_and_uid(omniauth['provider'],omniauth['uid'])
if authentication
flash[:success] = "Welcome back #{omniauth['info']['name']}!"
log_in(authentication.user)
redirect_to root_url
elsif current_user
current_user.authentications.create(provider: omniauth['provider'], uid: omniauth['uid'])
flash[:success] = "Welcome #{omniauth['info']['name']}!"
redirect_to root_url
else
user = User.new
user.authentications.build(:provider => omniauth ['provider'], :uid => omniauth['uid'])
if user.save
flash[:success] = "Signed in successfully."
redirect_to root_url
else
session[:omniauth] = omniauth.except('extra')
flash[:info] = "Just one step to go!"
redirect_to signup_url
end
end
end
While route.rb configuration file contains
match '/auth/:provider/callback' => 'authentication#create', via: [:get, :post]
Now I get stucked in writing down the controller test for two main reasons:
How can I make the post in test? With the following test unit
class AuthenticationControllerTest < ActionController::TestCase
test "post request" do
post :create
end
end
I get the following error
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"create", :controller=>"authentication"}
test/controllers/authentication_controller_test.rb:6:in `block in <class:AuthenticationControllerTest>'
test/controllers/authentication_controller_test.rb:6:in `block in <class:AuthenticationControllerTest>'
How can I create a "fake" env variable for the test?
Surfing the web I was able to find only tutorials using Capybara or Cucumber and nothing with the standard Rails tests.
Any kind of help will be really appreciated!
Andrea

Devise::InvitationsController reports Unpermitted parameters

I have a problem with devise_invitable 1.4.0 and strong parameters when I add additional custom parameters and I really hope somebody can guide me in the right direction. I am able to send invitations, but when an invited user accepts an invitation and enters a desired username, maiden name, password and confirmed password, the following error is shown:
Processing by Users::InvitationsController#update as HTML
Unpermitted parameters: username, name
The user is created as expected, but the 'username' and 'name' columns in the database are empty.
I have tried all the suggestions I could find for related issues, but none of the worked. I have noticed that if I change the app/controllers/users/invitations_controller.rb file in any way (eg inserting a blank space on an empty line) without restarting the webserver (Thin) the problem disappears - but the problem reappears when the webserver is restarted.
The various relevant files look like this:
routes.rb:
Rails.application.routes.draw do
root to: 'visitors#index'
#Tell rails to use the Devise controllers that were generated with this command:
# > rails generate devise:controllers users
#Using these generated controllers allows us to overwrite anything in the deault controllers.
devise_for :users, :path_names => {:sign_in => 'login', :sign_out => 'logout'}, controllers: {confirmations: "users/confirmations", passwords: "users/passwords", registrations: "users/registrations", sessions: "users/sessions", unlocks: "users/unlocks", :invitations => 'users/invitations'}
resources :users
end
config/initializers/devise.rb
Devise.setup do |config|
...
...
config.scoped_views = true
config.authentication_keys = [ :username ]
...
...
end
app/controllers/users/invitations_controller.rb:
class Users::InvitationsController < Devise::InvitationsController
private
# this is called when creating invitation
# should return an instance of resource class
def invite_resource
## skip sending emails on invite
resource_class.invite!(invite_params, current_inviter) do |u|
u.tenant = current_inviter.tenant
u.role = :user
end
end
def after_invite_path_for(resource)
users_path
end
def resource_params
params.permit(user: [:name, :email,:invitation_token, :username])[:user]
end
end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
around_filter :scope_current_tenant
before_filter :configure_permitted_parameters, if: :devise_controller?
if Rails.env.development?
# https://github.com/RailsApps/rails-devise-pundit/issues/10
include Pundit
# https://github.com/elabs/pundit#ensuring-policies-are-used
# after_action :verify_authorized, except: :index
# after_action :verify_policy_scoped, only: :index
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
end
#############################################################################
private
#############################################################################
if Rails.env.development?
def user_not_authorized
flash[:alert] = "Access denied." # TODO: make sure this isn't hard coded English.
redirect_to (request.referrer || root_path) # Send them back to them page they came from, or to the root page.
end
end
def current_tenant
#current_tenant ||= current_user.tenant unless current_user.nil?
end
helper_method :current_tenant
def scope_current_tenant(&block)
if current_tenant.nil?
scope_visitor_schema
yield
else
current_tenant.scope_schema("public", &block)
end
end
def scope_visitor_schema()
original_search_path = ActiveRecord::Base.connection.schema_search_path
ActiveRecord::Base.connection.schema_search_path = 'public'
ensure
ActiveRecord::Base.connection.schema_search_path = original_search_path
end
#############################################################################
protected
#############################################################################
def configure_permitted_parameters
# Only add some parameters
devise_parameter_sanitizer.for(:account_update).concat [:name, :email]
# Override accepted parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:name, :username, :password, :password_confirmation,
:invitation_token)
end
end
end
app/models/user.rb:
class User < ActiveRecord::Base
enum role: [:user, :admin]
after_initialize :create_tenant, :if => :new_record?
belongs_to :tenant
# has_many :invitations, :class_name => self.to_s, :as => :invited_by
scope :unconfirmed, -> { where(confirmed_at: nil) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
# validate :username, presence: true, uniqueness: true, format: { with: /[a-zA-Z0-9]{4,20}/ }
def displayed_username
username.nil? ? "N/A" : username
end
def displayed_name
name.nil? ? "N/A" : name.titleize
end
def create_tenant
#The create_tenant method will also be called when looking up a user,
#so the following ensures a tenant is only created if it does not already
#exist - and the user has not been invited and assigned to an existing tenant:
if self.tenant.nil?
#Set role to 'admin' if a tenant is about to be created:
self.role = :admin #if self.tenant.nil?
self.tenant = Tenant.new
end
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
end
I finally found a fix, which was to place the parameter sanitizer directly in users/invitations_controller.rb instead of the application_controller.rb.
class Users::InvitationsController < Devise::InvitationsController
before_filter :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:username, :name, :email, :password, :password_confirmation, :invitation_token)
end
end
end

Omniauth and strong parameters

So this is probably going to be a very dumb question but i've set up Oauth with twitter as the provider in a similar way to Ryan Bates's 'Simple Omniauth Railscast - my question is now that that is set up and working should i be setting strong parameters in my sessions controller or is this not necessary?
SessionsController.rb
class SessionsController < ApplicationController
def create
#user = User.find_by_uid(auth_hash[:uid]) || User.create_user(auth_hash)
session[:uid] = #user.id
if #user
redirect_to root_path
else
redirect_to root_path, flash: {signinerror: "Oops, something went wrong with your sign in. Please try again."}
end
end
def auth_hash
request.env['omniauth.auth']
end
def destroy
session[:uid] = nil
redirect_to root_path
end
end
User.rb
class User < ActiveRecord::Base
has_many :opinions
def self.create_user(auth_hash)
create do |user|
user.provider = auth_hash[:provider]
user.name = auth_hash[:info][:name]
user.uid = auth_hash[:uid]
user.username = auth_hash[:info][:nickname]
user.email = auth_hash[:info][:email]
user.image = auth_hash[:info][:image]
end
end
end
Thanks
Since you don't use mass assignment on object creation, strong parameters will not give you any additional security.
With this plugin Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted.
https://github.com/rails/strong_parameters

Create order after successful charge using stripe checkout

I am using stripe checkout for my payment solution within and e-commerce app. I want to create an order after a successful charge.
At the moment once payment is successful I just change a "success" boolean attribute on the current cart before destroying the cart. I will like to create an order instead so the cart object does not have to deal with this extra responsibility.
My problem is that I do not know where to tell my controller to create an order and how to pass data from the current cart to the order object.
Here is the charges controller setup
class ChargesController < ApplicationController
def new
#user = current_user
#cart = current_cart
#amount = #cart.total_price
end
def create
#amount = #cart.total_price
customer = Stripe::Customer.create(
:email => #user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => #user.id,
:amount => #amount,
:description => 'Rails Stripe customer',
:currency => 'usd'
)
# this changes the status of the current cart to success
#cart.update_status(current_cart)
session[:cart_id] = nil
redirect_to current_user, notice: 'You placed an order!'
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
end