How to set up a callback in an initializer? - ruby-on-rails

I'm using Koudoku for subscriptions. I want to do different things after receiving a Stripe webhook.
In the docs, it shows you can add a callback like so:
Koudoku.setup do |config|
config.subscriptions_owned_by = :user
config.stripe_publishable_key = ENV['STRIPE_PUBLISHABLE_KEY']
config.stripe_secret_key = ENV['STRIPE_SECRET_KEY']
# add webhooks
config.subscribe 'charge.failed', YourChargeFailed
end
What I can't figure out how to write the YourChargeFailed part. I've tried something like:
config.subscribe 'order.payment_succeeded', ActiveRecord::Subscription.after_successful_payment
but I get undefined method after_successful_payment for #<Class:0x007fb845849b30>
How can I successfully subscribe to Stripe events, capture the return data, and initiate a callback function?
Thanks!
UPDATE
Here is what I've tried, and the corresponding errors I'm receiving:
purchases_helper.rb
module PurchasesHelper
require 'stripe'
def stripe_webhook(event)
puts 'Purchases Helper'
puts 'invoice.payment_succeeded'
#customer = Stripe::Customer.retrieve(event[:data][:object][:customer])
#user = User.find_by(email: #customer[:email])
#badge = Badge.find_by(condition: '2019Purchase')
#badges_user = BadgesUser.find_by(user_id: #user.id, badge_id: #badge.id)
# if #badges_user === nil
# BadgesUser.create(user_id: user.id, badge_id: badge.id)
# end
puts 'badge created'
end
end
initializers/koudoku.rb
Koudoku.setup do |config|
include ::PurchasesHelper
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
::PurchasesHelper.stripe_webhook(event)
end
end
ERROR:
undefined method `stripe_webhook' for PurchasesHelper:Module excluded from capture: Not configured to send/capture in environment 'development'
NoMethodError (undefined method `stripe_webhook' for PurchasesHelper:Module):
Another attempt:
Koudoku.setup do |config|
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
PurchasesHelper.stripe_webhook(event)
end
end
ERROR:
undefined method `stripe_webhook' for PurchasesHelper:Module excluded from capture: Not configured to send/capture in environment 'development'
NoMethodError (undefined method `stripe_webhook' for PurchasesHelper:Module):
3rd Attempt:
Koudoku.setup do |config|
include PurchasesHelper
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
stripe_webhook(event)
end
end
ERROR:
A copy of PurchasesHelper has been removed from the module tree but is still active! excluded from capture: Not configured to send/capture in environment 'development'
ArgumentError (A copy of PurchasesHelper has been removed from the module tree but is still active!):

I see only one problem with your code.
module PurchasesHelper
require 'stripe'
def self.stripe_webhook(event) # NB self.
puts 'Purchases Helper'
puts 'invoice.payment_succeeded'
#customer = Stripe::Customer.retrieve(event[:data][:object][:customer])
#user = User.find_by(email: #customer[:email])
#badge = Badge.find_by(condition: '2019Purchase')
#badges_user = BadgesUser.find_by(user_id: #user.id, badge_id: #badge.id)
# if #badges_user === nil
# BadgesUser.create(user_id: user.id, badge_id: badge.id)
# end
puts 'badge created'
end
end
and then you call it by saying
Koudoku.setup do |config|
config.subscribe 'charge.succeeded' do |event|
puts 'charge created'
PurchasesHelper.stripe_webhook(event)
end
end
This should work
Wait but Why?!
Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits.
provide a namespace and prevent name clashes
implement the mixin facility (when you include them)
You've defined an instance method on the Module that when included it will appear on every instance of the object.
but you are not doing that in this case. You want to call stripe_webhook on the Module itself.
adding self. stripe_webhook in this case = PurchasesHelper. stripe_webhook which is the way to define a methods on the class/module.
You can even do more freaky stuff like:
class Animal
def self.all
%w[dog cat bird]
end
end
def Animal.include?(a)
self.all.include?(a)
end
Animal.include?('cat') # true
Animal.include?('virus') # false
so you can even define methods on the Animal class outside the scope of the class and it will work.
To sum up:
in this example:
module PurchasesHelper
def self.stripe_webhook(event)
#...
end
end
is equal to
module PurchasesHelper
def PurchasesHelper.stripe_webhook(event)
#...
end
end
which is why just adding self allows you to call PurchasesHelper.stripe_webhook

On Koudoku doc's, it says it actually uses stripe_event to handle that https://github.com/integrallis/stripe_event
So, looking on the strip_event examples, you can pass a block and do whatever you need or pass something that respond to the call method https://github.com/integrallis/stripe_event#usage

Related

How to correctly prepend module in a class?

I have got a AccountController class, this controller class is in the application kernel. I dont want to make changes into kernel, therefore Im going to Monkey Patch it.
Controller has method named successful_authentication, which I've rewritten. Inside the new method (in my module) this code calls new method named load_favourite_or_index.
I've read that alias_method_chain is deprecated now and should not be used. I'm trying to prepend my module before AccountController. But nothing happens, I guess my prepending code is not correct, please could you help me? Here's my code.
# frozen_string_literal: true
module RedmineKapDesign
module Patches
module AccountControllerPatch
def self.prepended(base) # :nodoc:
class << base
prepend InstanceMethods
end
end
module InstanceMethods
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
logger.info "Setting.autologin? = #{Setting.autologin?}, params[:autologin] = #{params[:autologin]}"
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user})
load_favourite_page_or_index
#redirect_back_or_default my_page_path
end
def load_favourite_page_or_index
user = User.current
favourite_page_field = CustomField.where(name: ["Favourite page", "favourite page", "favorite page", "Favourite page", "Любимая страница", "любимая страница", "Избранная страница", "избранная страница"]).first
page_url = user.custom_values.where(custom_field_id: favourite_page_field.id).first.value
if page_url.empty?
redirect_back_or_default my_page_path
else
redirect_to(page_url)
end
end
def self.hello
puts "Hello"
end
end
end
end
end
#unless AccountController.included_modules.include?(RedmineKapDesign::Patches::AccountControllerPatch)
# AccountController.send(:prepend, RedmineKapDesign::Patches::AccountControllerPatch)
#end
AccountController.singleton_class.prepend(RedmineKapDesign::Patches::AccountControllerPatch)
You don't need to separate out the instance methods into a seperate module. You can just place them in AccountControllerPatch and prepend whatever class you are monkeypatching with it.
module RedmineKapDesign
module Patches
module AccountControllerPatch
def successful_authentication(user)
logger.info "Successful authentication for '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}"
# Valid user
self.logged_user = user
logger.info "Setting.autologin? = #{Setting.autologin?}, params[:autologin] = #{params[:autologin]}"
# generate a key and set cookie if autologin
if params[:autologin] && Setting.autologin?
set_autologin_cookie(user)
end
call_hook(:controller_account_success_authentication_after, {:user => user})
load_favourite_page_or_index
#redirect_back_or_default my_page_path
end
def load_favourite_page_or_index
user = User.current
favourite_page_field = CustomField.where(name: ["Favourite page", "favourite page", "favorite page", "Favourite page", "Любимая страница", "любимая страница", "Избранная страница", "избранная страница"]).first
page_url = user.custom_values.where(custom_field_id: favourite_page_field.id).first.value
if page_url.empty?
redirect_back_or_default my_page_path
else
redirect_to(page_url)
end
end
def self.hello
puts "Hello"
end
end
end
end
Using a seperate module is only really needed for class methods when using the module mixin pattern. In framework code using a seperate InstanceMethods module can be used for organizational purposes. But in a simple monkeypatch it just makes more of a mess.
# config/initializers/my_monkey_patch.rb
# You should explicity require classes in initializers
require Rails.root.join('path', 'to', 'monkeypatch')
require Rails.root.join('path', 'to', 'target')
::AccountController.prepend RedmineKapDesign::Patches::AccountControllerPatch

Ruby on Rails 5.0 upgrade not working with rails console or db:migrate

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

Monkey patching Devise (or any Rails gem)

I'm using the Devise authentication gem in my Rails project, and I want to change the keys it's using in flash alerts. (Devise uses :notice and :alert flash keys, but I want to change them to :success and :error so that I can display nice green/red boxes with Bootstrap.)
So I want to be able to somehow override the set_flash_message method in DeviseController.
Here's the new method:
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
But I just don't know where to put it.
UPDATE:
Based on an answer I created a config/initializers/overrides.rb file with the following code:
class DeviseController
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
end
But this causes an error on every Devise action:
Routing Error: undefined method 'prepend_before_filter' for
Devise::SessionsController:Class
If you try to reopen a class, it's the same syntax as declaring a new class:
class DeviseController
end
If this code is executed before the real class declaration, it inherits from Object instead of extending the class declared by Devise. Instead I try to use the following
DeviseController.class_eval do
# Your new methods here
end
This way, you'll get an error if DeviseController has not been declared. As a result, you'll probably end up with
require 'devise/app/controllers/devise_controller'
DeviseController.class_eval do
# Your new methods here
end
Using Rails 4 #aceofspades answer didn't work for me.
I kept getting require': cannot load such file -- devise/app/controllers/devise_controller (LoadError)
Instead of screwing around with load order of initializers I used the to_prepare event hook without a require statement. It ensures that the monkey patching happens before the first request. This effect is similar to after_initialize hook, but ensures that monkey patching is reapplied in development mode after a reload (in prod mode the result is identical).
Rails.application.config.to_prepare do
DeviseController.class_eval do
# Your new methods here
end
end
N.B. the rails documentation on to_prepare is still incorrect: See this Github issue
In your initializer file :
module DeviseControllerFlashMessage
# This method is called when this mixin is included
def self.included klass
# klass here is our DeviseController
klass.class_eval do
remove_method :set_flash_message
end
end
protected
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
end
DeviseController.send(:include, DeviseControllerFlashMessage)
This is pretty brutal but will do what you want.
The mixin will delete the previous set_flash_message method forcing the subclasses to fall back to the mixin method.
Edit:
self.included is called when the mixin is included in a class. The klass parameter is the Class to which the mixin has been included. In this case, klass is DeviseController, and we call remove_method on it.
What about adding in the override initializer and alias for the attributes of the flash hash, like this:
class ActionDispatch::Flash::FlashHash
alias_attribute :success, :notice
alias_attribute :error, :alert
end
This should allow your application to read flash[:notice] or flash[:success](flash.notice and flash.success)
You need to overwrite DeviseController while keeping around its superclass, in your initializer.
Something like:
class DeviseController < Devise.parent_controller.constantize
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
end
I know this is an old thread but this might still be helpful. You should be able to require the file from the gem directory using the engine called_from path.
require File.expand_path('../../app/helpers/devise_helper',Devise::Engine.called_from)
require File.expand_path('../../app/controllers/devise_controller',Devise::Engine.called_from)
DeviseController.class_eval do
# Your new methods here
end
This is the kind of thing that you will want to put on initialize rails folder, because it's a custom config for this application in particular, second you should use like so:
class DeviseController
def set_flash_message(key, kind, options = {})
if key == 'alert'
key = 'error'
elsif key == 'notice'
key = 'success'
end
message = find_message(kind, options)
flash[key] = message if message.present?
end
end
then you should get the expected behavior.
hope it helps since i dont tested, of not pls give a feedback and i will help you try something diferent.

Rspec says implemented method is not implemented

Below is an rspec test I'm running to test another class I've made. Unfortunately the method I'm trying to test (delete) does not seem to be working. What's throwing me is that the error message I'm getting from the Termianl is:
/Users/user/Ruby/localWikiClient/localwiki_client/spec/delete_spec:11:in 'block (2 levels) in <top (required)>': undefined method 'delete' for #<Proc:0x007fe4739a5448> (NoMethodError)
However, this method is defined in the class. Below is the code:
require 'faraday'
require 'json/pure'
module Localwiki
##
# A client that wraps the localwiki api for a given server instance
#
class Client
attr_accessor :hostname # hostname of the server we'd like to point at
attr_reader :site_name # site resource - display name of wiki
attr_reader :time_zone # site resource - time zone of server, e.g. 'America/Chicago'
attr_reader :language_code # site resource - language code of the server, e.g. 'en-us'
def initialize hostname, user=nil, apikey=nil
#hostname = hostname
#user = user
#apikey = apikey
create_connection
collect_site_details
end
##
# Get site resource and set instance variables
#
def collect_site_details
site = fetch('site','1')
#site_name = site['name']
#time_zone = site['time_zone']
#language_code = site['language_code']
end
##
# create Faraday::Connection instance and set #site
#
def create_connection
#site = Faraday.new :url => #hostname
end
##
# delete a specific resource
# resources are "site", "page", "user", "file", "map", "tag", "page_tag"
# identifier is id, pagename, slug, etc.
def delete(resource,identifier)
case resource
when resource == "site"
#hostname = identifier
create_connection
when resouce == "user"
#hostname = list(identifier)
end
http_delete()
end
def http_delete()
response = #site.delete
puts response.to_s
end
Here's the rspec test I'm trying to run:
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
require 'localwiki_client'
describe '<siteinfo>.amazonaws.com/bears' do
subject { Localwiki::Client.new '<siteinfo>.compute-1.amazonaws.com/bears', '<username>', '[myApiKey]' }
context '#fetch' do
subject.delete('page', 'bears')
end
end
You can't access the subject like that within a context block. You will need to either put it in a before block or within an actual test block (it/specify):
describe '<siteinfo>.amazonaws.com/bears' do
subject { Localwiki::Client.new '<siteinfo>.compute-1.amazonaws.com/bears', '<username>', '[myApiKey]' }
context '#fetch' do
it "deletes the bears page" do
subject.delete('page', 'bears')
end
end
end

Ruby on Rails 2.3.8: Testing: How do I set up an instance variable to use throughout my tests?

Lets say I have some data that remanis the same throughout all of my tests, for forever and eternity. I create this data in setup. I store the data to #instance_var. But when I call #instance_var.attribute in any test, I get the following error:
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I know my instance variable isn't null, cause after it is set, I can do a puts #instance_var.inspect on it...
Any ideas?
EDIT:
setup do
user = Factory(:user)
account = Factory(:account)
set_user(user)
set_account(account)
puts "||||||||||||||||||||||||||||||||||||||||||||||" #this proves that the instance vars are not null
puts "| object_test.rb |"
puts "| #{#user.name} "
puts "| #{#account.name} "
puts "||||||||||||||||||||||||||||||||||||||||||||||"
end
A failing test (with the error above)
test "for detection of first content with multiple contents" do
object = Factory(:object, :user_id => #user.id, :account_id => #account.id)
... #the rest of this test isn't important, as it is the above line, on #user, where the nil.id error occers
in test_helper.rb
def set_user(user)
#user = user
end
def set_account(account)
#account = account
end
I don't really think I need these two methods, as when I define the #instance variable in setup, I get teh same result
in test_helper.rb there are some constants set fore ActiveSupport::TestCase:
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
fixtures :all
disabling these did nothing. =(
Have you tried
setup do
#user = Factory(:user)
#account = Factory(:account)
end
Normally, if you set the instance variables in the setup block, they should be available to all your tests. (You might be having an issue with scopes.)
My solution was to make a shared class, shared_test.rb
require 'test_helper'
class SharedTest
def self.initialize_testing_data
self.reset_the_database
self.set_up_user_and_account
# make sure our user and account got created
puts "|||||||||||||||||||||||||||||||||||||||||||||"
puts "| The user and account "
puts "| we'll be testing with:"
puts "| #{#user.name}"
puts "| #{#user.account.name}"
puts "|||||||||||||||||||||||||||||||||||||||||||||"
end
def self.reset_the_database
#clear the database and reset it
call_rake("db:test:prepare")
call_rake("db:bootstrap RAILS_ENV=test")
end
def self.set_up_user_and_account
#set up our user for doing all our tests (this person is very busy)
#user = Factory(:user)
#account = Factory(:account)
#user.account = #account
#user.save
end
end
so then at the top of every test file that needs user and account to stay the same between all the tests, you just do
require 'shared_test.rb'
and methods are called like
SharedTest.initialize_testing_data

Resources