I recently started getting complaints that locked users cannot reset their accounts from the email I am sending.
I'm getting
NameError (uninitialized constant Unlock):
config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'
Nothing has changed on my routesr and nothing has changed in my links... what could be causing this error message and how can I fix it?
quiet_assets.rb:
Rails.application.assets.logger = Logger.new('log/logger.txt')
Rails::Rack::Logger.class_eval do
def call_with_quiet_assets(env)
previous_level = Rails.logger.level
Rails.logger.level = Logger::ERROR if env['PATH_INFO'].index("/assets/") == 0
call_without_quiet_assets(env).tap do
Rails.logger.level = previous_level
end
end
alias_method_chain :call, :quiet_assets
end
Stacktrace:
INFO Started GET "/users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex" for 127.0.0.1 at 2018-11-22 21:08:56 +0200
INFO Processing by Devise::UnlocksController#show as HTML
INFO Parameters: {"unlock_token"=>"CzpxHwV5kL7EyDZb32Ex"}
INFO -- store_location: /users/unlock?unlock_token=CzpxHwV5kL7EyDZb32Ex
INFO Completed 500 Internal Server Error in 8.1ms
FATAL NameError (uninitialized constant Unlock):
config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'
INFO Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.7ms)
INFO Rendered /Users/david/.rvm/gems/ruby-1.9.3-p551/gems/actionpack-3.2.17/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
Controller
require 'white_label_utils'
include ERB::Util
#Need to include these helpers or image_path won't work
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
include ActionView::Helpers::AssetTagHelper
class CustomMailer < Devise::Mailer
include Devise::Mailers::Helpers
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
helper :application # gives access to all helpers defined within `application_helper`.
default from: 'no-reply#ourcompany.com'
# Sends a simple email (without attachments and call to action button)
# to - array of recipients
# subject - subject title of email
# message - body of email
# section - section used for white labeling purposes
# file_attachments - array of files attachments to be included in email. eg. [{name: 'myFileName.csv', content: fileData}, {name: 'anotherFileName.zip', content: anotherFileData}] Empty by default
def simple_email(to, subject, message, section = nil, file_attachments = [])
get_whitelabel_details(section)
set_email_global_params(to, subject, message)
# if there are files, attach them
file_attachments = [] if file_attachments.nil?
if file_attachments.length > 0
file_attachments.each do |file|
attachments["#{file[:name]}"] = file[:content]
end
end
mail(to: to, subject: #subject, from: #whitelabel.email_from).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
end
# Sends a devise invitation_instructions email located under app/views/user/mailer/invitation_instructions.html
def invitation_instructions(record, opts = {})
to = record.email
begin
section = record.publisher.sections.first
rescue => e
Rails.logger.warn "Could not find any sections associated with user #{record}"
section = nil
end
initialize_from_record(record)
get_whitelabel_details(section)
set_email_global_params(to, invitation_email_subject, I18n.t("devise.mailer.invitation_instructions.message", invitee_name: record.first_name.capitalize, inviter_name: #resource.invited_by.is_dy_admin ? #resource.invited_by.last_name.capitalize : #resource.invited_by.full_name, product_name: #whitelabel.product_name))
uri = URI.parse(edit_invitations_url({invitation_token: #resource.invitation_token}))
#cta_text = I18n.t("devise.mailer.invitation_instructions.call_to_action_text")
#cta_link = whitelabel_links(uri)
super record, opts
end
# Sends a devise unlock_instructions email located under app/views/user/mailer/unlock_instructions.html
def unlock_instructions(record, opts = {})
to = record.email
begin
section = record.publisher.sections.first
rescue => e
Rails.logger.warn "Could not find any sections associated with user #{record}"
section = nil
end
initialize_from_record(record)
get_whitelabel_details(section)
set_email_global_params(to, I18n.t("devise.mailer.unlock_instructions.subject"), I18n.t("devise.mailer.unlock_instructions.message"))
uri = URI.parse(unlock_url(#resource, :unlock_token => #resource.unlock_token))
#cta_text = I18n.t("devise.mailer.unlock_instructions.call_to_action_text")
#cta_link = whitelabel_links(uri)
super record, opts
end
# Sends a devise reset_password_instruction email located under app/views/user/mailer/reset_password_instructions.html
def reset_password_instructions(record, token = nil, opts = {})
to = record.email
begin
section = record.publisher.sections.first
rescue => e
Rails.logger.warn "Could not find any sections associated with user #{record}"
section = nil
end
initialize_from_record(record)
get_whitelabel_details(section)
set_email_global_params(to, I18n.t("devise.mailer.reset_password_instructions.subject"), I18n.t("devise.mailer.reset_password_instructions.message"))
uri = URI.parse(edit_password_url(#resource, {reset_password_token: #resource.reset_password_token}))
#cta_text = I18n.t("devise.mailer.reset_password_instructions.call_to_action_text")
#cta_link = whitelabel_links(uri)
super record, opts
end
private
def devise_mail(record, action, opts={})
initialize_from_record(record)
(mail headers_for(action, opts)).delivery_method.settings.merge!(Dynamic::Application.config.send_grid_smtp_settings)
end
# Customize the subject and sender display name by the white-label profile
def headers_for(action, opts)
headers = {:from => #email_from,
:reply_to => #email_from}
super.merge!(headers)
end
# Overrides the default subject line for devise emails (reset_password_instructions, invitation_instructions, etc)
def subject_for(key)
return super unless key.to_s == 'invitation_instructions'
invitation_email_subject
end
# Gets the whitelabel details associated with the section
def get_whitelabel_details(section)
#section = section.blank? ? err_message('section') : section
begin
#whitelabel = WhiteLabelUtils::get_profile(nil, section.site.publisher)
rescue => e
Rails.logger.warn "Could not determine WhiteLabel profile when sending email"
#whitelabel = WhiteLabelUtils::get_profile(nil, nil)
end
end
# Validates the existence of parameters and assigns them to global vars that will be used in the email template itself
def set_email_global_params(to, subject, message)
#errors = nil
#to = to.blank? ? err_message('to') : to
#subject = subject.blank? ? err_message('subject') : subject.slice(0, 1).capitalize + subject.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
#message = message.blank? ? err_message('message') : message.slice(0, 1).capitalize + message.slice(1..-1).chomp('.') #remove trailing period, we add this in the template so this avoids duplicates
#email_from = #whitelabel.email_from
#reply_to = #whitelabel.reply_to
#introduction = create_introduction(to)
unless #errors.blank?
raise #errors
end
end
def invitation_email_subject
I18n.t("devise.mailer.invitation_instructions.subject", product_name: #whitelabel.product_name)
end
# Receives a generic url and replaces it with whitelabelled domains
def whitelabel_links uri
"#{#whitelabel.root_url_info[:protocol]}://#{#whitelabel.root_url_info[:host]}#{uri.path}?#{uri.query}"
# rendered as https://companydomain.com/users/unlock?unlock_token=TOKENVALUE
end
# Searches the system for a user with 'email_address' and returns
# a personalized introduction with the user's first name otherwise
# returns a generic introduction with the wording 'Dear User'
def create_introduction email_address
user = User.where(email: email_address).first
"#{I18n.t("dy.common.general.hi")}#{user.nil? ? '' : " #{user.first_name.capitalize}"},"
end
def err_message(val)
#errors = #errors.blank? ? '<' + val + '> field can not be empty' : #errors + '\n<' + val + '> field can not be empty'
end
end
The route being called is https://example.com/users/unlock?unlock_token=USERS_TOKEN
and the controller is from devise unlockscontroller
class Devise::UnlocksController < DeviseController
prepend_before_filter :require_no_authentication
# GET /resource/unlock/new
def new
build_resource({})
end
# POST /resource/unlock
def create
self.resource = resource_class.send_unlock_instructions(resource_params)
if successfully_sent?(resource)
respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
else
respond_with(resource)
end
end
# GET /resource/unlock?unlock_token=abcdef
def show
self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
if resource.errors.empty?
set_flash_message :notice, :unlocked if is_navigational_format?
respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) }
else
respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
end
end
protected
# The path used after sending unlock password instructions
def after_sending_unlock_instructions_path_for(resource)
new_session_path(resource) if is_navigational_format?
end
# The path used after unlocking the resource
def after_unlock_path_for(resource)
new_session_path(resource) if is_navigational_format?
end
end
This was never properly resolved. I ended overriding the built-in devise callback functions (which is a horrible practice)
Old versions of Rails3 and Ruby 1.9.3 are most probably the culprits - but I don't have the privilege of upgrading - too much legacy code working in production already.
Story of my life :(
Related
I am using devise for authentication. I am overwriting devise token generator so that I can use 6 digit code and also overwriting it so that I can support mobile number confirmation.
If a user register with email and OTP is send via email. Registration seems to work fine. A user register with an email. An OTP is sent and after confirmation a user gets confirmed.
But when the user tries to update the email. I am using the same methods to send the confirmation code (as in registration which works fine) the user get saved in unconfirmed_email. A mail gets send in email but after confirmation a user email is not being copied to email field from unconfirmed_email field.
What could be the problem here.
app/services/users/confirmation_code_sender.rb
# frozen_string_literal: true
module Users
class ConfirmationCodeSender
attr_reader :user
def initialize(id:)
#user = User.find(id)
end
# rubocop :disable Metrics/AbcSize
def call
generate_confirmation_token!
if user.email?
DeviseMailer.confirmation_instructions(
user,
user.confirmation_token,
{ to: user.unconfirmed_email || user.email }
).deliver_now
else
Telco::Web::Sms.send_text(recipient: user.unconfirmed_mobile || user.mobile_number, message: sms_text)
end
end
# rubocop :enable Metrics/AbcSize
private
def generate_confirmation_token!
user.confirmation_token = TokenGenerator.token(6)
user.confirmation_sent_at = DateTime.current
user.save!(validate: false)
end
def sms_text
I18n.t('sms.confirmation_token', token: user.confirmation_token)
end
end
end
app/services/users/phone_or_email_updater.rb
# frozen_string_literal: true
module Users
class PhoneOrEmailUpdater < BaseService
def call
authorize!(current_user, to: :user?)
current_user.tap do |user|
user.update!(unconfirmed_mobile: params[:unconfirmed_mobile], unconfirmed_email: params[:unconfirmed_email])
ConfirmationCodeSender.new(id: user.id).call
end
end
end
end
config/nitializers/confirmable.rb
# frozen_string_literal: true
# Overriding this model to support the confirmation for mobile number as well
module Devise
module Models
module Confirmable
def confirm(args = {})
pending_any_confirmation do
return expired_error if confirmation_period_expired?
self.confirmed_at = Time.now.utc
saved = saved(args)
after_confirmation if saved
saved
end
end
def saved(args)
#saved ||= if pending_reconfirmation?
skip_reconfirmation!
save!(validate: true)
else
save!(validate: args[:ensure_valid] == true)
end
end
def pending_reconfirmation?
if unconfirmed_email.present?
self.email = unconfirmed_email
self.unconfirmed_email = nil
true
elsif unconfirmed_mobile.present?
self.mobile_number = unconfirmed_mobile
self.unconfirmed_mobile = nil
true
else
false
end
end
private
def expired_error
errors.add(
:email,
:confirmation_period_expired,
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago)
)
false
end
end
end
end
Mobile update seems to be working fine but email is not updating. I am using graphql to update the email
In console I tried using .confirm but it seems to be not working as well the user email is not getting confirmed
In your pending_reconfirmation?, self.unconfirmed_email is assigned to be nil. It seems like pending_reconfirmation? is only called in saved, however, it is called by pending_any_confirmation, too.
https://github.com/heartcombo/devise/blob/8593801130f2df94a50863b5db535c272b00efe1/lib/devise/models/confirmable.rb#L238
# Checks whether the record requires any confirmation.
def pending_any_confirmation
if (!confirmed? || pending_reconfirmation?)
yield
else
self.errors.add(:email, :already_confirmed)
false
end
end
So when the second time the pending_reconfirmation? is called in the saved, pending_reconfirmation? will return false because unconfirmed_email is nil.
You'd better not do actual assignments inside the methods end with ? it will be an implicit side-effect. Maybe create a new method end with ! to change the value of attributes.
For example:
module Devise
module Models
module Confirmable
def confirm(args = {})
pending_any_confirmation do
return expired_error if confirmation_period_expired?
self.confirmed_at = Time.now.utc
saved = saved(args)
after_confirmation if saved
saved
end
end
def saved(args)
#saved ||= if pending_reconfirmation?
reconfirm_email! if unconfirmed_email.present?
reconfirm_mobile! if unconfirmed_mobile.present?
skip_reconfirmation!
save!(validate: true)
else
save!(validate: args[:ensure_valid] == true)
end
end
def pending_reconfirmation?
unconfirmed_email.present? || nconfirmed_mobile.present?
end
def reconfirm_email!
self.email = unconfirmed_email
self.unconfirmed_email = nil
end
def reconfirm_mobile!
self.mobile_number = unconfirmed_mobile
self.unconfirmed_mobile = nil
end
private
def expired_error
errors.add(
:email,
:confirmation_period_expired,
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago)
)
false
end
end
end
end
Hi I have a controller file at app/controllers/lti_controller.rb and I need to add Test for the POST #launch endpoint which is defined as /lti/launch in routes.rb and working as intended.
Problem
I need to send some data to the /lti/launch in the encoding format x-www-form-urlencoded and by using Net::HTTP I am doing that from the spec file.
But as Net::HTTP requires host and port for their operation and I have given localhost:3000 as of now and which is going to the development environment.
Question
How to access the host and port of the test environment in Rspec 3.10 such that I can pass them in spec file ?
Is there any way to use post '/lti/launch' , req.body : data in x-www-form-urlencoded format` ? and also get the host and port of the test environment ?
My Spec file
spec/requests/lti_spec.rb
# frozen_string_literal: true
require "rails_helper"
require 'uri'
require 'net/http'
describe LtiController, type: :request do
before do
# shared keys for lms access
#oauth_consumer_key_fromlms = 'some_keys'
#oauth_shared_secret_fromlms = 'some_secrets'
end
describe "#launch" do
before do
# creation of assignment and required users
#mentor = FactoryBot.create(:user)
#group = FactoryBot.create(:group, mentor: #mentor)
#member = FactoryBot.create(:user)
FactoryBot.create(:group_member, user: #member, group: #group)
#assignment = FactoryBot.create(:assignment, group: #group, lti_consumer_key: #oauth_consumer_key_fromlms, lti_shared_secret: #oauth_shared_secret_fromlms, status: "open")
end
def launch_uri
launch_url = "http://0.0.0.0:3000/lti/launch"
URI(launch_url)
end
let(:parameters) {
{
'launch_url' => launch_uri().to_s,
'user_id' => #member.id,
'launch_presentation_return_url' => 'http://localhost:3000/tool_return',
'lti_version' => 'LTI-1p0',
'lti_message_type' => 'basic-lti-launch-request',
'resource_link_id' => '88391-e1919-bb3456',
'lis_person_contact_email_primary' => #member.email,
'tool_consumer_info_product_family_code' => 'moodle',
'context_title' => 'sample Course',
'lis_outcome_service_url' => 'http://localhost:3000/grade_passback',
'lis_result_sourcedid' => SecureRandom.hex(10)
}
}
def consumer_data(oauth_consumer_key_fromlms, oauth_shared_secret_fromlms, parameters)
consumer = IMS::LTI::ToolConsumer.new(oauth_consumer_key_fromlms, oauth_shared_secret_fromlms, parameters)
allow(consumer).to receive(:to_params).and_return(parameters)
consumer.generate_launch_data
end
context "lti parameters are valid" do
it "returns success if assignment key and secret are ok and group member is present" do
data = consumer_data(#oauth_consumer_key_fromlms, #oauth_shared_secret_fromlms, parameters)
response = Net::HTTP.post_form(launch_uri(), data)
expect(response.code).to eq("200")
end
end
end
end
My Controller file
app/controllers/lti_controller.rb
class LtiController < ApplicationController
skip_before_action :verify_authenticity_token, only: :launch # for lti integration
before_action :set_group_assignment, only: %i[launch]
before_action :set_lti_params, only: %i[launch]
after_action :allow_iframe_lti, only: %i[launch]
def launch
session[:is_lti]=true # the lti session starting
require 'oauth/request_proxy/action_controller_request'
if #assignment.blank?
# if no assignment is found
flash[:notice] = t("lti.launch.notice_no_assignment")
render :launch_error, status: 401
return
end
if #group.present? # if there is a valid group based for the lti_token_key
#provider = IMS::LTI::ToolProvider.new(
params[:oauth_consumer_key], # lms_oauth_consumer_key
#assignment.lti_shared_secret, # the group's lti_token
params
)
if !#provider.valid_request?(request) # checking the lti request from the lms end
render :launch_error, status: 401
return
end
lms_lti_host = URI.join #launch_url_from_lms, '/' # identifies the domain and saves in session
session[:lms_domain]=lms_lti_host
#user = User.find_by(email: #email_from_lms) # find user by matching email with circuitverse and lms
if #user.present? # user is present in cv
if #user.id == #group.mentor_id # user is teacher
sign_in(#user) # passwordless sign_in the user as the authenticity is verified via lms
lms_auth_success_notice = t("lti.launch.notice_lms_auth_success_teacher", email_from_lms: #email_from_lms, lms_type: #lms_type, course_title_from_lms: #course_title_from_lms)
redirect_to group_assignment_path(#group, #assignment), notice: lms_auth_success_notice # if auth_success send to group page
else
user_in_group = GroupMember.find_by(user_id:#user.id,group_id:#group.id) # check if the user belongs to the cv group
if user_in_group.present? # user is member of the group
# render the button
flash[:notice] = t("lti.launch.notice_students_open_in_cv")
create_project_if_student_present() # create project with lis_result_sourced_id for the student
render :open_incv, status: 200
else # user is not a member of the group
# send the user an email
flash[:notice] = t("lti.launch.notice_ask_teacher")
render :launch_error, status: 401
end
end
else # no such user in circuitverse,showing a notice to create an account in cv
flash[:notice] = t("lti.launch.notice_no_account_in_cv", email_from_lms: #email_from_lms )
render :launch_error, status: 400
end
else # there is no valid group present for the lti_consumer_key
flash[:notice] = t("lti.launch.notice_invalid_group")
render :launch_error, status: 400
end
end
def allow_iframe_lti
return unless session[:is_lti]
response.headers["X-FRAME-OPTIONS"] = "ALLOW-FROM #{session[:lms_domain]}"
end
def create_project_if_student_present
#user = User.find_by(email: #email_from_lms)
#project = Project.find_by(author_id: #user.id, assignment_id: #assignment.id) # find if the project is already present
if #project.blank? # if not then create one
#project = #user.projects.new
#project.name = "#{#user.name}/#{#assignment.name}"
#project.assignment_id = #assignment.id
#project.project_access_type = "Private"
#project.build_project_datum
#project.lis_result_sourced_id = params[:lis_result_sourcedid] # this param is required for grade submission
#project.save
end
end
private
def set_group_assignment # query db and check lms_oauth_consumer_key is equal to which assignment and find the group also
#assignment = Assignment.find_by(lti_consumer_key: params[:oauth_consumer_key])
if #assignment.present?
#group =#assignment.group
end
end
def set_lti_params # get some of the parameters from the lti request
#email_from_lms = params[:lis_person_contact_email_primary] # the email from the LMS
#lms_type = params[:tool_consumer_info_product_family_code] # type of lms like moodle/canvas
#course_title_from_lms = params[:context_title] # the course titile from lms
#launch_url_from_lms = params[:launch_presentation_return_url]
session[:lis_outcome_service_url] = params[:lis_outcome_service_url] # requires for grade submission
session[:oauth_consumer_key] = params[:oauth_consumer_key] # requires for grade submission
end
end
Sample data needed to be sent
{"oauth_consumer_key"=>"some_keys", "oauth_signature_method"=>"HMAC-SHA1", "oauth_timestamp"=>"1627879512", "oauth_nonce"=>"Id3rLYZBqMvnRuSpisMEEgFkLFnkxZPS2oqoyBJZLM", "oauth_version"=>"1.0", "context_title"=>"sample Course", "launch_presentation_return_url"=>"http://localhost:3000/tool_return", "launch_url"=>"http://0.0.0.0:3000/lti/launch", "lis_outcome_service_url"=>"http://localhost:3000/grade_passback", "lis_person_contact_email_primary"=>"chung#towne-littel.org", "lis_result_sourcedid"=>"3e87e3aa8f5056260a12", "lti_message_type"=>"basic-lti-launch-request", "lti_version"=>"LTI-1p0", "resource_link_id"=>"88391-e1919-bb3456", "tool_consumer_info_product_family_code"=>"moodle", "user_id"=>"461", "oauth_signature"=>"lgsHKJxHolBU1rTZ5M9zXg688hU="}
You don't need to know the host and port. And you should not perform the request directly with Net::HTTP manually. get/post/... helpers handle that. https://relishapp.com/rspec/rspec-rails/v/5-0/docs/request-specs/request-spec
I am trying to learn how to use Rails 5 (generally) but specifically, I'm trying to learn how to use service classes.
I'm trying to write a service class that maps a user's given email address (user's have an attribute called :email) to organisation's domain names. Organisations have attributes called :email_format. I use that attribute to hold the part of the email address that follows the "#".
When a user creates an account, I want to take their email address that they use to sign up with, and match the bit after the # to each of the organisations that I know about and try to find a matching one.
My attempts at this are plainly wrong, but I'm struggling to figure out why.
I have resources called User, Organisation and OrgRequest. The associations are:
User
belongs_to :organisation, optional: true
has_one :org_request
Organisation
has_many :org_requests
has_many :users
OrgRequest
belongs_to :user
belongs_to :organisation
I have tried to write a service class as:
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
if matching_organisation.present?
# user.organisation_request.new(organisation_id: matching_organisation.id)
# user.update_attributes!(organisation_id: matching_organisation.id)
else
#SystemMailer.unmatched_organisation(user: user).deliver_now
end
end
private
attr_accessor :user
def matching_organisation
# User::OrganisationMapperService.new(user).matching_organisation
User::OrganisationMapperService.new(user: user)
end
end
I then have an org requests controller with:
class Users::OrgRequestsController < ApplicationController
before_action :authenticate_user!, except: [:new, :create, :requested]
before_action :set_org_request, only: [:approved, :rejected, :removed]
# skip_before_action :redirect_for_unrequested_organisation
# skip_before_action :redirect_for_unknown_organisation
def index
organisation = Organisation.find_by(owner_id: current_user.id)
return redirect_to(user_path(current_user.id)) if organisation.nil?
#org_requests = organisation.org_requests
end
def new
#all_organisations = Organisation.select(:title, :id).map { |org| [org.title, org.id] }
#org_request = OrgRequest.new#form(OrganisationRequest::Create)
matched_organisation = User::OrganisationMapperService.new(current_user).matching_organisation
#org_request.organisation_id = matched_organisation.try(:id)
end
def create
#org_request = OrgRequest.new(org_request_params)
#org_request.user_id = current_user.id
if #org_request.save
OrgRequest::ProcessService.new(org_request).process
return redirect_to(user_path(current_user),
flash[:alert] => 'Your request is being processed.')
else
# Failure scenario below
#all_organisations = Organisation.select(:title, :id).map { |org| [org.title, org.id] }
render :new
end
end
def requested
# Need help - if this is contained in form inputs - how do i stop from overriding the submit path?
redirect_to(user_path(current_user))
#not sure about this - a similar redirect isnt required for articles or project create
end
def approve
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:approved)
flash[:notice] = "You've added this member."
redirect_to org_requests_path
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to :index
end
end
def remove
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:removed)
flash[:notice] = "Removed from the organisation."
redirect_to action: :index
# format.html { redirect_to :index }
# format.json { render :show, status: :ok, location: #project }
# redirect_to action: :show, id: project_id
# add mailer to send message to article owner that article has been approved
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to(user_path(current_user))
# redirect_to action: :show, id: project_id
end
end
def decline
#org_request = current_user.organisation.org_requests.find(params[:id])
if #org_request.state_machine.transition_to!(:declined)
flash[:notice] = "You're not eligible to join this organisation"
redirect_to action: :index
# redirect_back(fallback_location: root_path)
# format.html { redirect_to :index }
# redirect_to action: :show, id: organisation_request.profile
# add mailer to send message to article owner that article has been approved
else
flash[:error] = "You're not able to manage this organisation's members"
redirect_to(user_path(current_user))
# redirect_to action: :show, id: organisation_request.profile
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_org_request
#org_request = OrgRequest.find(params[:id])
authorize #org_request
end
# Never trust parameters from the scary internet, only allow the white list through.
def org_request_params
params.require(:org_request).permit(:organisation_id, :name) # Need help - not sure if I need to put user id and organisation id in this permission
end
end
I can't figure out another approach to this. When I try this, I get this error:
wrong number of arguments (given 1, expected 0)
The error message highlights line 7 of my service class, which has:
def initialize(user: u)
self.user = user
end
I have previously asked questions about this problem here: superclass mismatch for class User - inheriting from ActiveRecord::Base
but I haven't managed to catch the drift of the advice or what is causing the problem. This attempt is a mash up of suggestions that I have gleaned from at least 10 different tutorials - so I appreciate that its highly unlikely to be correct, but I'm struggling to understand how the different parts of this work to know what to try differently.
Can anyone give me a steer on how to try to progress this attempt?
Organisation mapper decorator has:
class User < ActiveRecord::Base
class OrganisationMapper < ::ApplicationDecorator
def matching_organisation
#matching_organisation ||= Organisation.by_email_format(email_format).first
end
def email_format
user.email.split('#').last
end
private
def user
#model
end
end
end
Application decorator has:
class ApplicationDecorator
def initialize(model)
#model = model
end
private
def method_missing(method, *args)
args.empty? ? #model.send(method) : #model.send(method, *args)
end
end
Org request service class has:
class OrgRequest::CreateService < ActiveRecord::Base
attr_accessor :org_request
def self.call(user_id: user_id, organisation_id: org_id)
new(user_id: user_id, organisation_id: organisation_id).call
end
def initialize(user_id: user_id, organisation_id: org_id)
self.user_id = user_id
self.organisation_id = organisation_id
end
def call
self.org_request \
= OrgRequest.new(user_id: current_user.id,
organisation_id: params[:org_request][:organisation_id])
if org_request.save
# send the email
true
else
false
end
end
end
NEXT ATTEMPT
I have tried every variation on this that I can think of. Nothing I'm trying makes any sense to me but I can't make sense out of any examples that I can find.
My service class currently has:
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
# if matching_organisation.present?
# user.org_request.new(organisation_id: matching_organisation.id)
# if found create a request for that user to enter the organisation
if match_domain.present?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
# user.update_attributes!(organisation_id: matching_organisation.id)
else
#SystemMailer.unmatched_organisation(user: user).deliver_now
end
end
private
attr_accessor :user
# def matching_organisation
# # User::OrganisationMapperService.new(user).matching_organisation
# User::OrganisationMapperService.new(user: user).Organisation.by_email_format(email_format).first
# end
# def matching_organisation
# #matching_organisation ||= Organisation.by_email_format(email_format).first
# end
def user_domain
user.email.split('#').last
end
def organisation_domain
#organisation = Organisation.find_by(email_format: user_domain)
end
# def user_email_domain
# # extract domain from users email
# user_email_domain = #user.email.split('#').last
# end
def match_domain
return unless #user_domain == #organisation.email_format
end
# find an organisation with a matching domain
# end
end
It's plainly wrong. The error message says:
NameError - undefined local variable or method `organisation' for #<User::OrganisationMapperService:0x007faec6ec06b8>
I can't make sense of the error message either because I have put '#' in front of every instance of 'organisation' just to try to make that error go away. It doesn't.
Please help.
ANOTHER COMPLETELY SENSELESS ERROR MESSAGE
I had another go at trying to write the method to check whether an email domain matches an organisation's email format in my service class.
The call method now has:
def call
if user_domain == Organisation.email_format.any?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
else
end
end
The error message in the console says:
NoMethodError - undefined method `email_format' for #<Class:0x007faec72d8ac0>
That has to be nonsense because my organisation table has an attribute in it called :email_format. In the console, I can write:
o = Organisation.first.email_format
Organisation Load (3.3ms) SELECT "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1 [["LIMIT", 1]]
That gives me the result I'm looking for.
I'm trying (to my wits end) to learn how rails communicates. I can't make any sense of any of it.
NEXT ATTEMPT
Next guess of a go at the call method:
def call
if user_domain == organisation_domain?
OrgRequest.create(user: #user, organisation_id: #organisation_domain.organisation.id) #if organisation
else
end
Produces this error:
NoMethodError - undefined method `organisation_domain?' for #<User::OrganisationMapperService:0x007faec3be3600>:
I can't seem to find a single form of expression that doesnt produce this error.
The problem appears to be in the following line:
matched_organisation = User::OrganisationMapperService.new(current_user).matching_organisation
It should be this instead:
matched_organisation = User::OrganisationMapperService.new(user: current_user).matching_organisation
I had a session on code mentor. This is the answer. I hope it might help someone else who is trying to learn.
class User::OrganisationMapperService #< ActiveRecord::Base
def self.call(user: u)
new(user: user).call
end
def initialize(user: u)
self.user = user
end
def call
if organisation_domain.present?
OrgRequest.create(user: #user, organisation_id: organisation_domain.id) #if organisation
else
end
end
private
attr_accessor :user
def user_domain
user.email.split('#').last
end
def organisation_domain
#organisation ||= Organisation.find_by(email_format: user_domain)
end
end
After recently upgrading to Ruby on Rails 5.0 from 4.2, I have been unable to run rake db:migrate, or rails console. I think it best to solve the console error first and it seems to give the followin more informative errors:
Here is the full stack trace if I correctly understand your request. Thank you.
$ rails c
/Users/my_username/.rvm/gems/ruby-2.3.0/gems/actionpack-5.0.0/lib/action_controller/test_case.rb:49:in `initialize': wrong number of arguments (given 0, expected 2) (ArgumentError)
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/view_context/build_strategy.rb:41:in `new'
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/view_context/build_strategy.rb:41:in `block in controller'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/andand-1.3.3/lib/andand.rb:60:in `me'
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/view_context/build_strategy.rb:40:in `controller'
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/view_context/build_strategy.rb:30:in `call'
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/view_context.rb:49:in `build'
from /Users/my_username/.rvm/gems/ruby-2.3.0/bundler/gems/draper-57a514133bc2/lib/draper/railtie.rb:63:in `block in <class:Railtie>'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/railtie.rb:226:in `block in run_console_blocks'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/railtie.rb:247:in `each'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/railtie.rb:247:in `each_registered_block'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/railtie.rb:226:in `run_console_blocks'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/application.rb:463:in `block in run_console_blocks'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/engine/railties.rb:13:in `each'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/engine/railties.rb:13:in `each'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/application.rb:463:in `run_console_blocks'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/engine.rb:442:in `load_console'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-5.0.0/lib/rails/commands/console.rb:34:in `initialize'
from /Users/my_username/.rvm/gems/ruby-2.3.0/gems/railties-
--clipped 441 characters to enter stack overflow question edit
I understand that the test_case.rb part of the actionpack-5.0.0 gem expects two arguments. After opening this code in Textmate I see the 'initialize' method is part of the TestRequest Class in the following test_case.rb file:
require 'rack/session/abstract/id'
require 'active_support/core_ext/hash/conversions'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/hash/keys'
require 'action_controller/template_assertions'
require 'rails-dom-testing'
module ActionController
# :stopdoc:
class Metal
include Testing::Functional
end
module Live
# Disable controller / rendering threads in tests. User tests can access
# the database on the main thread, so they could open a txn, then the
# controller thread will open a new connection and try to access data
# that's only visible to the main thread's txn. This is the problem in #23483
remove_method :new_controller_thread
def new_controller_thread # :nodoc:
yield
end
end
# ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
# Please use ActionDispatch::IntegrationTest going forward.
class TestRequest < ActionDispatch::TestRequest #:nodoc:
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
DEFAULT_ENV.delete 'PATH_INFO'
def self.new_session
TestSession.new
end
# Create a new test request with default `env` values
def self.create
env = {}
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
env["rack.request.cookie_hash"] = {}.with_indifferent_access
new(default_env.merge(env), new_session)
end
def self.default_env
DEFAULT_ENV
end
private_class_method :default_env
def initialize(env, session)
super(env)
self.session = session
self.session_options = TestSession::DEFAULT_OPTIONS
#custom_param_parsers = {
xml: lambda { |raw_post| Hash.from_xml(raw_post)['hash'] }
}
end
def query_string=(string)
set_header Rack::QUERY_STRING, string
end
def content_type=(type)
set_header 'CONTENT_TYPE', type
end
def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
non_path_parameters = {}
path_parameters = {}
parameters.each do |key, value|
if query_string_keys.include?(key)
non_path_parameters[key] = value
else
if value.is_a?(Array)
value = value.map(&:to_param)
else
value = value.to_param
end
path_parameters[key] = value
end
end
if get?
if self.query_string.blank?
self.query_string = non_path_parameters.to_query
end
else
if ENCODER.should_multipart?(non_path_parameters)
self.content_type = ENCODER.content_type
data = ENCODER.build_multipart non_path_parameters
else
fetch_header('CONTENT_TYPE') do |k|
set_header k, 'application/x-www-form-urlencoded'
end
case content_mime_type.to_sym
when nil
raise "Unknown Content-Type: #{content_type}"
when :json
data = ActiveSupport::JSON.encode(non_path_parameters)
when :xml
data = non_path_parameters.to_xml
when :url_encoded_form
data = non_path_parameters.to_query
else
#custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
data = non_path_parameters.to_query
end
end
set_header 'CONTENT_LENGTH', data.length.to_s
set_header 'rack.input', StringIO.new(data)
end
fetch_header("PATH_INFO") do |k|
set_header k, generated_path
end
path_parameters[:controller] = controller_path
path_parameters[:action] = action
self.path_parameters = path_parameters
end
ENCODER = Class.new do
include Rack::Test::Utils
def should_multipart?(params)
# FIXME: lifted from Rack-Test. We should push this separation upstream
multipart = false
query = lambda { |value|
case value
when Array
value.each(&query)
when Hash
value.values.each(&query)
when Rack::Test::UploadedFile
multipart = true
end
}
params.values.each(&query)
multipart
end
public :build_multipart
def content_type
"multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
end
end.new
private
def params_parsers
super.merge #custom_param_parsers
end
end
class LiveTestResponse < Live::Response
# Was the response successful?
alias_method :success?, :successful?
# Was the URL not found?
alias_method :missing?, :not_found?
# Was there a server-side error?
alias_method :error?, :server_error?
end
# Methods #destroy and #load! are overridden to avoid calling methods on the
# #store object, which does not exist for the TestSession class.
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
def initialize(session = {})
super(nil, nil)
#id = SecureRandom.hex(16)
#data = stringify_keys(session)
#loaded = true
end
def exists?
true
end
def keys
#data.keys
end
def values
#data.values
end
def destroy
clear
end
def fetch(key, *args, &block)
#data.fetch(key.to_s, *args, &block)
end
private
def load!
#id
end
end
# Superclass for ActionController functional tests. Functional tests allow you to
# test a single controller action per test method. This should not be confused with
# integration tests (see ActionDispatch::IntegrationTest), which are more like
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
# different HTTP requests).
#
# == Basic example
#
# Functional tests are written as follows:
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
# an HTTP request.
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
# the controller's HTTP response, the database contents, etc.
#
# For example:
#
# class BooksControllerTest < ActionController::TestCase
# def test_create
# # Simulate a POST response with the given HTTP parameters.
# post(:create, params: { book: { title: "Love Hina" }})
#
# # Asserts that the controller tried to redirect us to
# # the created book's URI.
# assert_response :found
#
# # Asserts that the controller really put the book in the database.
# assert_not_nil Book.find_by(title: "Love Hina")
# end
# end
#
# You can also send a real document in the simulated HTTP request.
#
# def test_create
# json = {book: { title: "Love Hina" }}.to_json
# post :create, json
# end
#
# == Special instance variables
#
# ActionController::TestCase will also automatically provide the following instance
# variables for use in the tests:
#
# <b>#controller</b>::
# The controller instance that will be tested.
# <b>#request</b>::
# An ActionController::TestRequest, representing the current HTTP
# request. You can modify this object before sending the HTTP request. For example,
# you might want to set some session properties before sending a GET request.
# <b>#response</b>::
# An ActionDispatch::TestResponse object, representing the response
# of the last HTTP response. In the above example, <tt>#response</tt> becomes valid
# after calling +post+. If the various assert methods are not sufficient, then you
# may use this object to inspect the HTTP response in detail.
#
# (Earlier versions of \Rails required each functional test to subclass
# Test::Unit::TestCase and define #controller, #request, #response in +setup+.)
#
# == Controller is automatically inferred
#
# ActionController::TestCase will automatically infer the controller under test
# from the test class name. If the controller cannot be inferred from the test
# class name, you can explicitly set it with +tests+.
#
# class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
# tests WidgetController
# end
#
# == \Testing controller internals
#
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
# can be used against. These collections are:
#
# * session: Objects being saved in the session.
# * flash: The flash objects currently in the session.
# * cookies: \Cookies being sent to the user on this request.
#
# These collections can be used just like any other hash:
#
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
# assert flash.empty? # makes sure that there's nothing in the flash
#
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
#
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
# action call which can then be asserted against.
#
# == Manipulating session and cookie variables
#
# Sometimes you need to set up the session and cookie variables for a test.
# To do this just assign a value to the session or cookie collection:
#
# session[:key] = "value"
# cookies[:key] = "value"
#
# To clear the cookies for a test just clear the cookie collection:
#
# cookies.clear
#
# == \Testing named routes
#
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
#
# assert_redirected_to page_url(title: 'foo')
class TestCase < ActiveSupport::TestCase
module Behavior
extend ActiveSupport::Concern
include ActionDispatch::TestProcess
include ActiveSupport::Testing::ConstantLookup
include Rails::Dom::Testing::Assertions
attr_reader :response, :request
module ClassMethods
# Sets the controller class name. Useful if the name can't be inferred from test class.
# Normalizes +controller_class+ before using.
#
# tests WidgetController
# tests :widget
# tests 'widget'
def tests(controller_class)
case controller_class
when String, Symbol
self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
when Class
self.controller_class = controller_class
else
raise ArgumentError, "controller class must be a String, Symbol, or Class"
end
end
def controller_class=(new_class)
self._controller_class = new_class
end
def controller_class
if current_controller_class = self._controller_class
current_controller_class
else
self.controller_class = determine_default_controller_class(name)
end
end
def determine_default_controller_class(name)
determine_constant_from_test_name(name) do |constant|
Class === constant && constant < ActionController::Metal
end
end
end
# Simulate a GET request with the given parameters.
#
# - +action+: The controller action to call.
# - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
# - +body+: The request body with a string that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
#
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
# +post+, +patch+, +put+, +delete+, and +head+.
# Example sending parameters, session and setting a flash message:
#
# get :show,
# params: { id: 7 },
# session: { user_id: 1 },
# flash: { notice: 'This is flash message' }
#
# Note that the request method is not verified. The different methods are
# available to make the tests more expressive.
def get(action, *args)
res = process_with_kwargs("GET", action, *args)
cookies.update res.cookies
res
end
# Simulate a POST request with the given parameters and set/volley the response.
# See +get+ for more details.
def post(action, *args)
process_with_kwargs("POST", action, *args)
end
# Simulate a PATCH request with the given parameters and set/volley the response.
# See +get+ for more details.
def patch(action, *args)
process_with_kwargs("PATCH", action, *args)
end
# Simulate a PUT request with the given parameters and set/volley the response.
# See +get+ for more details.
def put(action, *args)
process_with_kwargs("PUT", action, *args)
end
# Simulate a DELETE request with the given parameters and set/volley the response.
# See +get+ for more details.
def delete(action, *args)
process_with_kwargs("DELETE", action, *args)
end
# Simulate a HEAD request with the given parameters and set/volley the response.
# See +get+ for more details.
def head(action, *args)
process_with_kwargs("HEAD", action, *args)
end
def xml_http_request(*args)
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
xhr and xml_http_request methods are deprecated in favor of
`get :index, xhr: true` and `post :create, xhr: true`
MSG
#request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
#request.env['HTTP_ACCEPT'] ||= [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
__send__(*args).tap do
#request.env.delete 'HTTP_X_REQUESTED_WITH'
#request.env.delete 'HTTP_ACCEPT'
end
end
alias xhr :xml_http_request
# Simulate an HTTP request to +action+ by specifying request method,
# parameters and set/volley the response.
#
# - +action+: The controller action to call.
# - +method+: Request method used to send the HTTP request. Possible values
# are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+. Can be a symbol.
# - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
# - +body+: The request body with a string that is appropriately encoded
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
# - +session+: A hash of parameters to store in the session. This may be +nil+.
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
# - +format+: Request format. Defaults to +nil+. Can be string or symbol.
#
# Example calling +create+ action and sending two params:
#
# process :create,
# method: 'POST',
# params: {
# user: { name: 'Gaurish Sharma', email: 'user#example.com' }
# },
# session: { user_id: 1 },
# flash: { notice: 'This is flash message' }
#
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
# prefer using #get, #post, #patch, #put, #delete and #head methods
# respectively which will make tests more expressive.
#
# Note that the request method is not verified.
def process(action, *args)
check_required_ivars
if kwarg_request?(args)
parameters, session, body, flash, http_method, format, xhr = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr)
else
http_method, parameters, session, flash = args
format = nil
if parameters.is_a?(String) && http_method != 'HEAD'
body = parameters
parameters = nil
end
if parameters || session || flash
non_kwarg_request_warning
end
end
if body
#request.set_header 'RAW_POST_DATA', body
end
if http_method
http_method = http_method.to_s.upcase
else
http_method = "GET"
end
parameters ||= {}
if format
parameters[:format] = format
end
#html_document = nil
self.cookies.update #request.cookies
self.cookies.update_cookies_from_jar
#request.set_header 'HTTP_COOKIE', cookies.to_header
#request.delete_header 'action_dispatch.cookies'
#request = TestRequest.new scrub_env!(#request.env, #request.session)
#response = build_response #response_klass
#response.request = #request
#controller.recycle!
#request.set_header 'REQUEST_METHOD', http_method
parameters = parameters.symbolize_keys
generated_extras = #routes.generate_extras(parameters.merge(controller: controller_class_name, action: action.to_s))
generated_path = generated_path(generated_extras)
query_string_keys = query_parameter_names(generated_extras)
#request.assign_parameters(#routes, controller_class_name, action.to_s, parameters, generated_path, query_string_keys)
#request.session.update(session) if session
#request.flash.update(flash || {})
if xhr
#request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'
#request.fetch_header('HTTP_ACCEPT') do |k|
#request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
end
end
#request.fetch_header("SCRIPT_NAME") do |k|
#request.set_header k, #controller.config.relative_url_root
end
begin
#controller.recycle!
#controller.dispatch(action, #request, #response)
ensure
#request = #controller.request
#response = #controller.response
#request.delete_header 'HTTP_COOKIE'
if #request.have_cookie_jar?
unless #request.cookie_jar.committed?
#request.cookie_jar.write(#response)
self.cookies.update(#request.cookie_jar.instance_variable_get(:#cookies))
end
end
#response.prepare!
if flash_value = #request.flash.to_session_value
#request.session['flash'] = flash_value
else
#request.session.delete('flash')
end
if xhr
#request.delete_header 'HTTP_X_REQUESTED_WITH'
#request.delete_header 'HTTP_ACCEPT'
end
#request.query_string = ''
#response.sent!
end
#response
end
def controller_class_name
#controller.class.anonymous? ? "anonymous" : #controller.class.controller_path
end
def generated_path(generated_extras)
generated_extras[0]
end
def query_parameter_names(generated_extras)
generated_extras[1] + [:controller, :action]
end
def setup_controller_request_and_response
#controller = nil unless defined? #controller
#response_klass = ActionDispatch::TestResponse
if klass = self.class.controller_class
if klass < ActionController::Live
#response_klass = LiveTestResponse
end
unless #controller
begin
#controller = klass.new
rescue
warn "could not construct controller #{klass}" if $VERBOSE
end
end
end
#request = TestRequest.create
#response = build_response #response_klass
#response.request = #request
if #controller
#controller.request = #request
#controller.params = {}
end
end
def build_response(klass)
klass.create
end
included do
include ActionController::TemplateAssertions
include ActionDispatch::Assertions
class_attribute :_controller_class
setup :setup_controller_request_and_response
end
private
def scrub_env!(env)
env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
env.delete 'action_dispatch.request.query_parameters'
env.delete 'action_dispatch.request.request_parameters'
env
end
def process_with_kwargs(http_method, action, *args)
if kwarg_request?(args)
args.first.merge!(method: http_method)
process(action, *args)
else
non_kwarg_request_warning if args.any?
args = args.unshift(http_method)
process(action, *args)
end
end
REQUEST_KWARGS = %i(params session flash method body xhr)
def kwarg_request?(args)
args[0].respond_to?(:keys) && (
(args[0].key?(:format) && args[0].keys.size == 1) ||
args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
)
end
def non_kwarg_request_warning
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
ActionController::TestCase HTTP request methods will accept only
keyword arguments in future Rails versions.
Examples:
get :show, params: { id: 1 }, session: { user_id: 1 }
process :update, method: :post, params: { id: 1 }
MSG
end
def document_root_element
html_document.root
end
def check_required_ivars
# Sanity check for required instance variables so we can give an
# understandable error message.
[:#routes, :#controller, :#request, :#response].each do |iv_name|
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
end
end
end
def html_format?(parameters)
return true unless parameters.key?(:format)
Mime.fetch(parameters[:format]) { Mime['html'] }.html?
end
end
include Behavior
end
# :startdoc:
end
Here is the error when I run rake db:migrate:
rake aborted!
NameError: uninitialized constant ActiveSupport
/Users/my_username/projects/zoner/config/application.rb:1:in `
Here is the application.rb file's first lines:
ActiveSupport.halt_callback_chains_on_return_false = false
require File.expand_path('../boot', __FILE__)
require File.expand_path('../initializers/decent_exposure', __FILE__)
require 'rails/all'
require 'active_support'
require 'active_support/core_ext'
If there is someone that can help identify the errors in this code that will help me add the necessary arguments to the TestRequest class that would be very helpful. Thank you.
Update from #scott-w (August 2016):
The correct location is now: gem 'draper', github: 'drapergem/draper'
the rails-5 branch has been merged into master and no longer exists
Original: The issue with rails console error is caused by draper gem as seen from the trace. As it stands now, master (main) branch of draper doesn't support Rails 5. It seems to be know for some long time, but before Rails 5 was officially released as stable, it wasn't really put action on.
Try this fork with few fixes embedded ("old" solution)
gem 'draper', github: 'audionerd/draper', branch: 'rails5'
Taken from here
Or better (seems to become current solution)
gem 'draper', github: 'drapergem/draper', branch: 'rails-5'
The work on this kind of official branch is going as we speak :)
https://github.com/drapergem/draper/commits/rails-5
Or get rid of draper for Rails 5, as it doesn't support it in its master branch yet (seems to change in the coming days)
Update -> rails-5 branch was merged into master so Rails 5 are now supported
I have the following code:
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :current_user
def facebook_cookies
#facebook_cookies ||= Koala::Facebook::OAuth.new.get_user_info_from_cookie(cookies).symbolize_keys!
end
def current_user
begin
# allow for ?access_token=[TOKEN] for iOS calls.
#access_token = params[:access_token] || facebook_cookies[:access_token]
#graph = Koala::Facebook::API.new(#access_token)
# TODO: move this to session[:current_user]..
#current_user ||= User.from_graph #graph.get_object('me', { fields: 'id,first_name,last_name,gender,birthday' })
rescue
nil # not logged in
end
end
def authenticate
redirect_to(root_url) if current_user.nil?
end
end
(I have setup Koala as described here https://github.com/arsduo/koala/wiki/Koala-on-Rails)
I don't really want to introduce OmniAuth as what I am trying to do is fairly simple. The above code works, the problem is that it is calling Facebook for every page load = not good. I'm guessing I need to store session[:user_id] and then just call User.find(session[:user_id]) for each subsequent request after the user has authenticated?
Can anyone suggest the most efficient way of solving this so I'm not waiting for Facebook on each page load?
You could try something like this:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :current_user, if: Proc.new{ !current_user? }
def facebook_cookies
#facebook_cookies ||= Koala::Facebook::OAuth.new.get_user_info_from_cookie(cookies).symbolize_keys!
end
def current_user
begin
# allow for ?access_token=[TOKEN] for iOS calls.
#access_token = params[:access_token] || facebook_cookies[:access_token]
#graph = Koala::Facebook::API.new(#access_token)
# TODO: move this to session[:current_user]..
#current_user ||= User.from_graph #graph.get_object('me', { fields: 'id,first_name,last_name,gender,birthday' })
rescue
nil # not logged in
end
end
def authenticate
redirect_to(root_url) if current_user.nil?
end
def current_user?
!!#current_user
end
end
I have an implementation that is use for my facebook authentication and authorization.
The code is seperated in non state changing methods and statechanging methods.
Feel free to use the code.
## checks if we have access to fb_info and it is not expired
## Non stagechanging
def oauth_is_online
return !(session[:fb_cookies].nil? or session[:fb_cookies]['issued_at'].to_i + session[:fb_cookies]['expires'].to_i < Time.now.to_i - 10)
end
## checks if the user is in the database
## Non stats changing
def oauth_is_in_database
return oauth_is_online && 0 < User.where(:fb_id => session[:fb_cookies]['user_id']).count
end
## Returns true if it is the specified user
## Non stagechanging
def oauth_is_user(user)
return oauth_is_online && session[:fb_cookies]['user_id'] == user.fb_id
end
## Requires the user to be online. If not, hell breaks loose.
def oauth_require_online
if !oauth_ensure_online
render :file => "public/401.html", :status => :unauthorized, :layout => false
return false
end
return session[:fb_cookies]
end
## Requires the user to be online and the correct user. If not, hell breaks loose.
def oauth_require_user(user)
c = oauth_require_online
return false unless c
return c unless !oauth_is_user(user)
render :file => "public/403.html", :status => :unauthorized, :layout => false
return false
end
## Ensures that the user is online
def oauth_ensure_online
begin
# Checks if the user can be verified af online
if !oauth_is_in_database
# the user is not online. Now make sure that they becomes online.
logger.info "Creates new FB Oauth cookies"
fb_cookies = Koala::Facebook::OAuth.new.get_user_info_from_cookie(cookies)
# TODO In the future the session should be reset at this point
# reset_session
session[:fb_cookies_reload] = false
session[:fb_cookies] = fb_cookies
logger.info "Updating user in database."
# Tries to load the user
#current_user = User.where(:fb_id => session[:fb_cookies]['user_id']).first
logger.info "User exists? => #{#current_user.nil?}"
# creating if not found
#current_user = User.new(:fb_id => session[:fb_cookies]['user_id']) unless !#current_user.nil?
# Loading graph
#fb_graph = Koala::Facebook::API.new(session[:fb_cookies]['access_token'])
# Loading info from graph
me = #fb_graph.get_object('me')
#current_user.name_first = me['first_name'].to_s
#current_user.name_last = me['last_name'].to_s
#current_user.email = me['email'].to_s
#current_user.fb_access_token = session[:fb_cookies]['access_token'].to_s
# Saving the updated user
#current_user.save!
return oauth_is_online
else
# the user is online. Load variables.
#current_user = User.where(:fb_id => session[:fb_cookies]['user_id']).first
#fb_graph = Koala::Facebook::API.new(#current_user.fb_access_token)
end
return oauth_is_online
rescue Koala::Facebook::OAuthTokenRequestError => oe
## TODO handle the diferent errors
# user is not online on facebook
# user have not authenticatet us
# token is used allready once.
logger.debug "FB koala OAuthTokenRequestError: #{oe}"
return false
end
end