rspec: Helpers Method on Model undefined method - ruby-on-rails

I'm getting an issue with helpers method included in my model.
I include my helpers as follow:
class Booking < ApplicationRecord
include BookingsHelper
include PaymentsHelper
Both of BookingsHelper and PaymentsHelper have slack_notify function.
So in order to call the good slack_notify function I call this function as below:
BookingsHelper.slack_notify(self)
PaymentsHelper.slack_notify(requester.email, 'danger', 'booking:proceed_payment', e.message)
When I run my test (with rspec) I got:
Failure/Error: BookingsHelper.slack_notify(self)
NoMethodError:
undefined method `slack_notify' for BookingsHelper:Module
And I noticed if I change: BookingsHelper.slack_notify(self) by slack_notify(self) it works but call the slack_notify in PaymentsHelper so I don't really understand what's happening. And if I remove the PaymentHelper it call the good one
If someone could highlight me on this behavior, I would be really interested to understand whats going on
Thanks

You are using Mixin here.
A mixin can basically be thought of as a set of code that can be added
to one or more classes to add additional capabilities without using
inheritance. In Ruby, a mixin is code wrapped up in a module that a
class can include or extend
You do not access helper methods like the static methods but you call them directly, in your example you should call slack_notify directly without having module name before.
When you include two modules which have the same method name then the last one overrides the previous one.
If you do not want it to be overriden then you have to define in the module like that:
def BookingsHelper.slack_notify
// your code
end
and
def PaymentsHelper.slack_notify
// your code
end
see the example about sin and cos here: https://www.tutorialspoint.com/ruby/ruby_modules.htm
Read more about mixins and you will have better understanding of what is going on here.

Related

RIght way of writing module methods in Ruby

what is right way of writing module? is it only used to stock some peace of code to minimize the number of lines, or is it something much more important than that
I have used and seen ways of writing module, I am working on setting up correct way to define and standardised module. this example is kind of controller code that we use in rails
Way 1 :-
module B
extend ActiveSupport::Concern
def process_items
# do somthing...
#items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items
end
end
Way 2 :-
module B
extend ActiveSupport::Concern
def process_items(items)
# do somthing...
items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items(#items)
end
end
Way 1 :-
When I see this independently, its not much readable as I don't know how #items appeared in this method
Unit testing would be hard for method as its dependent
Way 2 :-
Looking at method I can see input is coming we are processing it and returning it back (readablity is good)
Unit testing is easy to this, we wll call method pass what it needs and expect
The way I see modules should be independent, self explanatory, it should be generic so that can be used in any class, kind of helpers. But other way could be dependent on where we use modules
We are using modules like in rails
We use conccern in models, when we call module method we can use self.<field> we don't need to pass anything because instance variable is supposed to be accesssable in every instance method
View helpers are modules I see they put logic into it hard to understand how the variable come from may be instance variable or params, what about making it method which accept somthing and return it back
Concerns on controllers, like the example I have given
I would like to have thoughts on this, what is best approach out of it? is it something which can be standarise or it is more situational or I don't know yet :)
Note: -
I was looking at this question but answer given on this question is no more valid as referenced links are not working.
Right Way to Use Module
The difference here is practically academic, as if you have attr_reader :x then both #x and x will have the same meaning.
It's understood that within a mixin module you will be referencing methods and/or variables that are part of the class or module doing the "mixing in". As such, seeing #x, or in your case, #items, should not come as a real surprise.
If you want to add it as an explicit argument you're sort of missing a lot of the benefits of using a mixin in the first place. You don't need to mix it in at all, you can just use it like B.process_items(...). In other words, your second approach is having an identity crisis. Is it a stand-alone module that includes Concern for no reason, or a weak mixin?
When it comes to testing, you must test the mixin in a module or class which implements the required features. In this case you need either an #items variable, or an items method, and that must have a value of the expected type.
This should be documented somewhere for clarity, but is effectively an implicit contract with anyone using this module.

Retrieve list of instance methods that are defined in rails class excluding inherited and included methods

I have a class Product in my rails project, I am trying to retrieve a list of instance methods of my class that are defined in my file (not inherited method or included via mixin). Here's a small sample of my class :
class Product
include Mongoid::Document
include Mongoid::Paperclip
include Mongoid::Search
include Mongoid::Slug
include Mongoid::Timestamps
extend Enumerize
def product_image
image.url(:small) unless image.nil?
end
def product_school_level
self.school_levels.join ' | '
end
def product_grades
self.grades.where(degree_ids: nil).pluck(:name).uniq.join ' | '
end
end
I tried to use Product.instance_methods(false). However this still returns a lot of methods I dont need, here's a little sample :
:_run_post_process_callbacks,
:aliased_fields,
:_post_process_callbacks,
:_run_image_post_process_callbacks,
:_image_post_process_callbacks,
:_validation_callbacks,
:nested_attributes?,
:_run_touch_callbacks,
:readonly_attributes?,
:_run_save_callbacks,
:aliased_fields?,
:_save_callbacks,
:localized_fields?,
:readonly_attributes,
:fields?,
:pre_processed_defaults?,
:_update_callbacks,
:post_processed_defaults?,
:fields,
:_id_default
I ran Product.new.method(:_run_post_process_callbacks).source_location on a few of these methods to try to check where they come from. It seems they all come from active_support.
I never included active_support in my class, so I guess classes in a rails project automatically include active_supports methods ? How is that possible without any inheritance syntax (<<) or include syntax ?
How can I then achieve what I want to do and get rid of these methods I dont need in my list ?
Many (most? all?) of those extra methods you are seeing are being created using Module#define_method. If you dig deep in the source for active_support, you'll see that. (You aren't directly including active_support, but it's being pulled in by one or more of the Mongoid modules.)
So these are in fact valid instance methods of your model class, which is why they are included in instance_methods(false). Other methods that are defined "conventionally" in the mixins, such as #freeze, are reported by instance_methods(true), but not by instance_methods(false).
I think you may have to do something to filter the list based on the source location. Something along these lines:
my_methods = Product.instance_methods(false).select do |m|
Product.instance_method(m).source_location.first.ends_with? '/product.rb'
end

Why do I get "uninitialized constant" error when referencing a class?

Here is how my data structure looks like
Controller
API
V1
Controller1.rb
Controller2.rb
Serializers
Model1Serializer.rb
Model2Serializer.rb
I'm trying to access the serializers in my Controllers
Here is my Controller
class API::V1::Controller1 < ApplicationController
require_relative 'model1_serializer'
def doStuff
render json:MyData, each_serializer:Model1Serializer
end
end
Here is my serializer
class API::V1::Serializers::Model1Serializer < ActiveModel::Serializer
# Code here for serializing
end
I'm getting the following error. Why does it think Model1Serializer is under Controller 1?
uninitialized constant API::V1::Controller1::Model1Serializer
If In my Controller i change Model1Serializer to API::V1::Serializers::Model1Serializer then it works, except I don't want to be dependent on V1 in my namespace, that way If I decide to move the code to V2 I don't end up changing the code to point to V2. What's the best way to handle this?
"Why does it think Model1Serializer is under Controller 1:
Strictly speaking that is not actually the case. When looking up a constant, Ruby checks multiple namespaces (if they available) but the error message only mentions the innermost one.
Why do I get “uninitialized constant” error when referencing a class?
To answer this, it is important to understand how Ruby looks up constants. Assuming you have this namespace hierarchy:
module API
module V1
class Controller1
end
end
end
If you are accessing Model1Serializer from Controller1 Ruby checks the following nested namespaces:
API::VI::Controller1::Model1Serializer
API::VI::Model1Serializer
API::Model1Serializer
::Model1Serializer
But Model1Serializer is defined in API::VI::Serializers::Model1Serializer which is not included in this list. That is why Ruby can't find it.
To fix this, you should change the offending line to include the sub-module:
render json: MyData, each_serializer: Serializers::Model1Serializer
But most likely it still won't work because you are using "shortcut namespaces", i.e. API::V1 instead of module API; module V1; ...; end; end This prevents Ruby from searching parent namespaces because they are not added to the nested modules list.
In other words, only the following namespaces are checked by Ruby:
API::VI::Controller1::Serializers::Model1Serializer
::Serializers::Model1Serializer
You can access the module hierarchy by calling Module.nesting at the desired code location.
Disclaimer: There is a lot more to Ruby constant lookup than presented here.
Your controller is API::V1::Controller1, but your serializer is API::V1::Serializers::Model1Serializer.
In your doStuff method, you try to look up Model1Serializer -- but there's no way to see that class from this point in the namespace.
Try using API::V1::Serializers::Model1Serializer instead there.
Update: The OP edited his question:
If In my Controller i change Model1Serializer to API::V1::Serializers::Model1Serializer then it works, except I don't want to be dependent on the Vq in y namespace, that way If I decide to move the code to V2 I don't end up changing the code to point to V2. What's the best way to handle this?
A quick way is to define Current = V1 in your API module, then reference API::Current::... when referencing your models from the controllers.
A better way is to think carefully about why you need to simultaneously provide multiple APIs in the same application like this, and whether that's really the right way to version it. (That's probably outside the scope of a single SO answer, though, and will be too dependent on your specific application.)

Metaprogramming in ActionMailer::Base

I am actually creating a newsletter massmailling software from which I can create a new mailling list on the fly, upload a template, and then send an email to the suscribers from that list doing something like this:
#suscribers.each do |suscriber|
NewsletterMailer.delay.send("#{#list.name}_newsletter", suscriber, #newsletter)
end
(Note that the delay method is because I use sidekiq for my background jobs)
I have tried to override the method_missing from ActionMailer::Base inside the NewsletterMailer class to handle this logic, but it just doesn't seem to be executed. I just receive a NoMethodError saying "undefined method `testing_newsletter' for NewsletterMailer:Class".
I looked up the ActionMailer::Base#method_missing source code and I see that it is already executing some logic so we can be able to do Mailer.name_of_the_email.deliver without calling new. Also, this method is protected.
But, is there a way I can still send an email from a method that is not hardcoded inside my NewsletterMailer class? Is there a way to add methods dynamically to an ActionMailer controller?
Thanks a lot.
If you've defined it as def method_missing ..., you have created an instance method, but your code indicates you are sending the dynamic message to the class itself. You need to define self.method_missing on NewsletterMailer if you want it to execute as you've written it.
That being said, I would recommend against this design for your particular case. The idea I would keep in mind is that you have behavior - the methods and actions that all newsletters have in common - and data, which describes the particulars of any given list and/or its newsletter. In most cases, these should remain separate and distinct, with your code describing behavior, and a database holding your data. It makes little sense to define a new method for each mailing list, when you could just have a method send_newsletter that takes the list as an argument.
You can use class_eval to define new methods on the fly:
class SomeClass
end
[:foo, :bar].each do |name|
SomeClass.class_eval <<COMMAND
def self.#{name}
puts "Hello from #{name}"
end
COMMAND
end
SomeClass.foo
SomeClass.bar

Module.new with class_eval

This is a large commit. But I want you to concentrate on this change block. http://github.com/rails/rails/commit/d916c62cfc7c59ab6411407a05b946d3dd7535e9#L2L1304
Even without understanding the full context of the code I am not able to think of a scenario where I would use
include Module.new {
class_eval <<-RUBY
def foo
puts 'foo'
end
RUBY
}
Then end result is that in the root context (self just before include Module.new) a method called foo has been added.
If I take out the Module.new code and if I only leave class_eval in that case also I will have a method called foo in self.
What am I missing.
If you dig in the documentation you find that including a module will add the methods therein only if they are not already defined. So this approach will not overwrite the method in case it is already there.
This ActiveRecord code has been asked about in another question, where it received an excellent answer. https://stackoverflow.com/a/3473479/420947
However, the simplified eval string here removes the motivation to write this code, which is why it appears confusing. In the unchanged code, the block binding captures a local variable used to reflect on the association: #{reflection.name}.clear.

Resources