Rails.application.routes.draw do
resources :carts
...
end
Now in controller, I can
cart_url(#cart)
or in view, I can
<%= link_to 'New Cart', new_cart_path %>
In which context are cart_url and new_cart_path defined? (how to see those in rails console)
In rails console, I tried:
irb(main):053:0> ApplicationController.new.rails_postmark_inbound_emails_path
Traceback (most recent call last):
2: from (irb):53
1: from (irb):53:in `rescue in irb_binding'
NoMethodError (undefined method `host' for nil:NilClass)
irb(main):058:0> CartsController.new.carts_url
Traceback (most recent call last):
2: from (irb):58
1: from (irb):58:in `rescue in irb_binding'
NoMethodError (undefined method `host' for nil:NilClass)
irb(main):056:0> CartsController.new_line_item_url
Traceback (most recent call last):
2: from (irb):56
1: from (irb):56:in `rescue in irb_binding'
NoMethodError (undefined method `new_line_item_url' for CartsController:Class)
The context for the route helpers is actually an anomynous module.
# This code was abbreviated for StackOverflow
module ActionDispatch
# A NamedRouteCollection instance is a collection of named routes, and also
# maintains an anonymous module that can be used to install helpers for the
# named routes.
class NamedRouteCollection
include Enumerable
attr_reader :routes, :url_helpers_module, :path_helpers_module
private :routes
def initialize
#routes = {}
#path_helpers = Set.new
#url_helpers = Set.new
#url_helpers_module = Module.new
#path_helpers_module = Module.new
end
def add(name, route)
key = name.to_sym
path_name = :"#{name}_path"
url_name = :"#{name}_url"
if routes.key? key
#path_helpers_module.undef_method path_name
#url_helpers_module.undef_method url_name
end
routes[key] = route
helper = UrlHelper.create(route, route.defaults, name)
define_url_helper #path_helpers_module, path_name, helper, PATH
define_url_helper #url_helpers_module, url_name, helper, UNKNOWN
#path_helpers << path_name
#url_helpers << url_name
end
def get(name)
routes[name.to_sym]
end
def key?(name)
return unless name
routes.key? name.to_sym
end
alias []= add
alias [] get
alias clear clear!
def each(&block)
routes.each(&block)
self
end
def names
routes.keys
end
def length
routes.length
end
# Given a +name+, defines name_path and name_url helpers.
# Used by 'direct', 'resolve', and 'polymorphic' route helpers.
def add_url_helper(name, defaults, &block)
helper = CustomUrlHelper.new(name, defaults, &block)
path_name = :"#{name}_path"
url_name = :"#{name}_url"
#path_helpers_module.module_eval do
redefine_method(path_name) do |*args|
helper.call(self, args, true)
end
end
#url_helpers_module.module_eval do
redefine_method(url_name) do |*args|
helper.call(self, args, false)
end
end
#path_helpers << path_name
#url_helpers << url_name
self
end
end
# ...
end
This module is created before the request is passed to a controller when ActionDispatch reads the route file and builds a routes collection.
And the controller then includes both the url_helpers and path_helpers modules. How this method ends up in the view is a topic upon itself but the gist is that ActionView::Context provides the methods and instance variables of the controller as the context (self) to the templating engine (ERB, Slim, Haml, jBuilder etc).
You can call the route helpers in the console via app. Thats because the console session is actually an integration test (🤯).
irb(main):006:0> app.root_path
=> "/"
irb(main):007:0> app.root_url
=> "http://www.example.com/"
When you do:
irb(main):053:0> ApplicationController.new.rails_postmark_inbound_emails_path
Traceback (most recent call last):
2: from (irb):53
1: from (irb):53:in `rescue in irb_binding'
You get an error since controllers are not meant to be used isolation. That nil:NilClass that host is called on is actually supposed to be a request.
Controllers are Rack applications and they operate by being instanciated and then dispatching a request:
ctrl = PetsController.new
ctrl.dispatch(name, request, response)
Related
I've got simple service which creates a record called PropertyReport:
#app/services/portfolios/related_property_reports.rb
module Portfolios
class RelatedPropertyReports
def initialize(portfolio, portfolio_report)
#portfolio = portfolio
#portfolio_report = portfolio_report
end
def call
PropertyReport.create!(
property: property,
portfolio_report: portfolio_report,
)
end
end
end
Now I want to test this service using below minitest:
#app/services/portfolios/related_property_reports_test.rb
require 'test_helper'
module Portfolios
class RelatedPropertyReports < ActiveSupport::TestCase
setup do
#portfolio = Portfolio.create(name: Faker::Bank.name)
#property = Property.create(portfolio: #portfolio, name: Faker::Bank.name, status: 'planned')
#portfolio_report = PortfolioReport.create(portfolio: #portfolio)
end
test 'create new record' do
service.call
assert_equal 1, PropertyReport.count
end
private
def service
#service ||= ::Portfolios::RelatedPropertyReports.new(#portfolio, #portfolio_report)
end
end
end
Which gives me an error:
Portfolios::RelatedPropertyReports#test_create_new_record:
ArgumentError: wrong number of arguments (given 2, expected 1)
Super weird considering I gives two arguments inside of new. What did I missed?
you need to use a different class name or module for your test class. Right now you are overriding the original class object.
irb(main):001:1* module Test
irb(main):002:2* class Test
irb(main):003:3* def initialize(a, b)
irb(main):004:2* end
irb(main):005:1* end
irb(main):006:0> end
=> :initialize
irb(main):007:1* module Test
irb(main):008:2* class Test
irb(main):009:3* def initialize(a)
irb(main):010:2* end
irb(main):011:3* def service
irb(main):012:3* ::Test::Test.new(1,2)
irb(main):013:2* end
irb(main):014:1* end
irb(main):015:0> end
=> :service
irb(main):016:0> Test::Test.new(1).service
(irb):9:in `initialize': wrong number of arguments (given 2, expected 1) (ArgumentError)
from (irb):12:in `new'
from (irb):12:in `service'
from (irb):16:in `<main>'
from /home/drewb/.rubies/ruby-3.0.1/lib/ruby/gems/3.0.0/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
from /home/drewb/.rubies/ruby-3.0.1/bin/irb:23:in `load'
from /home/drewb/.rubies/ruby-3.0.1/bin/irb:23:in `<main>'
We are having a production problem while sending emails using action mailer.
NameError
undefined local variable or method `to_ary' for #<Mail::Part:0x0000000008e0c998>
Did you mean? to_addrs
Error appears totally random and behaves completely same way like https://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
What I want to do is to monkey patch the missing_method to make it work.
So I created some tests including
let(:part) { Mail::Part.new }
it 'returns self' do
allow_any_instance_of(Mail::Message).to receive(:method_missing).and_raise(NameError, 'error part')
expect([part].flatten).to eq [part]
end
and created monkey patch
require 'mail'
module Mail
class Part < Message
# monkey patched due to same behaviour like
# https://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/v
def method_missing(name, *args, &block)
begin
super
pp :hello
rescue NameError => e
return [self] if name.try(:to_sym) == :to_ary
raise e
end
end
end
end
The intention of this test is that Array#flatten calls to_ary on it's content. Mail::Part#method_missing is normally not defined, but I created one to handle possible NameError from Mail::Message#method_missing and return the right value.
The problem is, that Mail::Part#method_missing is called, super is called and raises NameError, but rescue does not handle anything. pp :hello is skipped, because of the raised error.
So the test ends up with
NameError: error part
0) Mail::Part call flatten with NameError returns self
Failure/Error: expect([part].flatten).to eq [part]
NameError:
error part
# ./spec/lib/core_ext/mail/part_spec.rb:13:in `flatten'
# ./spec/lib/core_ext/mail/part_spec.rb:13:in `block (4 levels) in <top (required)>'
# ./spec/active_record_spec_helper.rb:19:in `block (2 levels) in <top (required)>'
Another problem also is, that the test ends up in infinite recursion, when I try
def method_missing(name, *args, &block)
return [self]
end
The same with
def method_missing(name, *args, &block)
begin
raises_name_error
rescue NameError => e
return [self] if name.try(:to_sym) == :to_ary
raise e
end
end
def raises_name_error
raise NameError, 'error part'
end
but in this case, the rescue block handles the NameError.
Any idea for solution?
Resolved by
module Mail
class Part < Message
def to_ary
[]
end
end
end
My initial tries for solution were little overkill.
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
I have a form to edit a page, while it tells it's not a variable from what I have known from related questions. In view, an error is raised from this line:
<%= form_for #wiki, :url => giki_path(#wiki.name), :html => { :method => :put } do |f| %>
Where the #wiki does seem to be an instance, which can be confirmed by:
$ rails console
> #wiki
#<Gollum::Page:70026260995800 Home (markdown) #wiki="path/to/git/wiki/.git">
> #wiki.name
"/wiki/Home"
So I don't understand what is causing the problem:
undefined method `model_name' for #<Gollum::Page:0x007f6084d2bdb0>
Edit:
In controller:
# giki_controller.rb
def edit
#wiki = Wiki.find(params[:id])
end
# the same method, worked fine
def show
#wiki = Wiki.find(params[:id])
end
In model:
# wiki.rb
class Wiki
include ActiveModel::AttributeMethods
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :raw_data, :formatted_data, :title, :path, :change_desc, :versions
# Gollum Init
WIKI = Gollum::Wiki.new(Settings.wiki_repo, :base_path => "/wiki")
# initialize
def initialize(attributes = {})
attributes.each do |key, value|
send("#{key}=", value)
end
end
# no database
def persisted?
false
end
def self.find(name)
WIKI.page(name) # find a page by name
end
First lines from logger:
NoMethodError - undefined method `model_name' for #<Gollum::Page:0x007f607dfec4e8>:
actionpack (4.2.6) lib/action_controller/model_naming.rb:9:in `model_name_from_record_or_class'
actionview (4.2.6) lib/action_view/record_identifier.rb:47:in `dom_class'
Full traceback: I created a gist.
Your backtrace says that model_name is undefined in <Gollum::Page:0x007f607dfec4e8> which is an instance of Gollum::Page.
Reason
form_for method internally calls model_name method. This is actually a valid method name in ActiveRecord's instance.
Try
User.first.model_name
This model_name is not present in #wiki since this is not an instance of Wiki its rather the instance of Gollum::Page.
How can I say that?
Well, I saw you have overridden the self.find method in Wiki
def self.find(name)
WIKI.page(name) # find a page by name
end
so in your edit action, you have used find method to get the persisted instance, which will hand you over an instance Gollum::Page and this is not expected by form_for helper method.
Solution (Edited)
Well, if you were using ActiveRecord and wanted to continue the overridden self.find method then you can use where or find_by_x method instead in edit action. Like
def edit
#wiki = Wiki.find_by_id(params[:id])
end
But looks like you are not using ActiveRecord or your model is not derived from it, so you have to use the form_for method in different fashion.
If you don't need to attach a form to a model instance, then check out ActionView::Helpers::FormTagHelper#form_tag.
form_tag(giki_path(#wiki.name), method: :put)
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