Why do I get "uninitialized constant" error when referencing a class? - ruby-on-rails

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.)

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.

Check if a ruby Class is in a particular Module

I have a controller object with controller.class == Admin::TeamsController. I might also have a circumstance like controller.class == Admin::UsersController. Now I want to check if this is true:
controller.class.to_s.match?('Admin::')
I.e., I want to know: Is this object of a class that's defined within the Admin module namespace? To spell that out, is the structure like the following?
module Admin
module SomeOtherModulePerhaps
class TeamsController
end
end
end
My question: Is there a nicer Ruby way to test for this? It feels kind of hacky to convert the class to a string, then do a regex match like that.
EDIT:
For my constrained use case, I could check like this:
controller.class.to_s.split('::').first == 'Admin'
But that doesn't quite solve the general case that other people might have. For example, there might be cases like XyzAdmin::TeamsController that one might want to exclude, on which my first solution fails, or Foo::Admin::TeamsController that one might want to include, on which my second solution fails.
I'd like to find a better way.
Rails comes with module_parents:
module Admin
module SomeOtherModulePerhaps
class TeamsController
end
end
end
controller = Admin::SomeOtherModulePerhaps::TeamsController.new
controller.class.module_parents
#=> [Admin::SomeOtherModulePerhaps, Admin, Object]
controller.class.module_parents.include?(Admin)
#=> true
Under the hood, it uses Module#name, i.e. "Admin::SomeOtherModulePerhaps::TeamsController".
How about
controller.class.const_defined?(:Admin)
returns true or false
What about to use controller_path
https://api.rubyonrails.org/classes/AbstractController/Base.html#method-c-controller_path
controller_path.match?('admin')
You might try playing with Module#nesting, but it’d return rather unexpected results depending on whether the class was defined using fully qualified name or a set of nesting statements.
After all, class names in ruby are simple constants, and one might define the class name in many ways, like:
module A
def self.class!
Class.new do |c|
define_method :test do puts c.name end
end
end
end
A.const_set :C, A.class!
#⇒ A::C
A::C.new.test
#⇒ A::C
Which roughly means, there are tons of ways to fool the best detection mechanism. That said, I’d go with the easiest one.
controller.class.to_s.split('::')[0...-1].include?('Admin')
Any occurrence of Admin would be counted, save for when Admin is the last item in the class name chain.
I want to know: Is this object of a class that's defined within the Admin module namespace?
[...]
Is there a nicer Ruby way to test for this?
Classes aren't defined in modules, therefore, there is neither a nice way nor any other way to test for it.
When you write a class definition body inside a module definition body, you do not create any relationship whatsoever between the module and the class. The only relationship is between the constant that the class gets assigned to and the module, not the class.
Therefore, since this relationship does not exist, you cannot test for it.

Monkey patching a core class with business logic with Rails

I have a monkeypatched of ActiveRecord find with some business logic, for example:
# lib/core_extensions/active_record/finder_methods/finder.rb
module ActiveRecord
module FinderMethods
def find(*args)
return super if block_given?
#... business logic code => my_error_control = true
raise "My Error" if my_error_control
retorn = find_with_ids(*args)
end
end
end
retorn
I have not seen many examples like this, and this causes me a doubt:
Where should finder.rb be?
In this example, this file is in lib/core_extensions/... but if it contains business logic, I think finder.rb should lives in the folder app/core_extensions/ isn't it?
Edited, after Sergio Answer
things like this, are a bad practice?
# lib/core_extensions/nil_class/image_attributes.rb
# suport for product images attributes
class NilClass
def main_image(size,evita_video)
"/images/paperclip_missing/original/missing.png"
end
end
Where should finder.rb be?
Ultimately, it doesn't matter. It only matters that this code gets loaded. This mix of patching base libraries and adding business logic there looks like something that MUST be documented thoroughly (in the project's wiki or something like that). And if it is documented, then it doesn't matter. The code is where the documentation says it is.
That being out of the way, here's a design suggestion:
when user seeks a Family Family.find(params[family_id],session[:company_id]), this find will compare the company of the family result family.company witht the parameter
Why not do something like this:
family = current_company.families.find(params[:family_id])
where current_company can be defined as #current_company ||= Company.find(session[:company_id])
Here, if this company doesn't have this family, you'll get an exception.
Same effect*, only without any patching. Much more futureproof. You can even add a couple of rubocop rules to ensure that you never write a naked Family.find.
* it's not like you add that patch and rest of your code magically acquires super-powers. No. You still have to change all the finders, to pass that company id.
It's the first time I see such case :). I'd put it in app/core_extensions and check if live reloading works correctly with it. If not, I'd move it to lib/. (It's just a heuristic)
Edit:
Instead of extending NilClass I'd rather use regular NullObjects. It's really less surprising and easier to understand.
https://robots.thoughtbot.com/rails-refactoring-example-introduce-null-object

Cannot cache from decorator (draper)

Caching is by far the most logic-intensive part of my view code, so I would like to do fragment caching from inside a decorator, however, I cant do it.
When i do this from my decorator:
def cached_name
h.cache do
"a name here"
end
end
I get this:
You have a nil object when you didn't expect it! You might have
expected an instance of Array. The error occurred while evaluating
nil.length
I instantiate my decorator from inside a controller
#presenter = SomePresenter::new
I am using HAML for my views
How can I succesfully cache from inside my decorator, so my view can do stuff like this
= #decorator.cached_logic_heavy_stuff
UPDATE: I have created a git repo showing my issue: https://github.com/houen/presenter_caching
UPDATE: This maybe works - see the repo
include Haml::Helpers
def another_way_to_try
self.init_haml_helpers
buffer = haml_buffer.buffer
h.with_output_buffer(buffer) do
h.cache do
h.concat "i should still not be empty"
end
end
end
I'd suggest using Rails.cache directly might solve your problem; we do the same thing in our decorators with Rails 4.
def cached_name
Rails.cache.fetch(source) do
source.name # etc.
end
end
If you're using Draper, I believe you don't need to explicitly pass the view context. You will likely want to pass a model or collection to your draper present when you instantiate. Examples:
class UserDecorator < Draper::Base
decorates :user
# additional methods
end
# in the controller
#presenter = UserDecorator.new(#user) # for an instance
#presenter = UserDecorator.decorate(#users) # for a collection
I suspect the nil object error you're getting is coming from another method call that's not listed in your code.
As for fragment caching from your decorator, you'll want to use the concat helper method to get this to work inside the decorator:
# your decorator class
def cached_name
h.cache("some_cache_key") do
h.concat "a name here"
end
end
Rails' cache method tries to infer a cache key based on the view that it's being called from. Since you're not actually calling it from a view (but from inside an instance of a decorator class), I expect that it's bombing when trying to build a cache key.
You might try passing a cache key explicitly, via h.cache "your cache key" do. With a full stack trace, you can figure out where it's throwing the exception, and then work around that, as well. Without the full stack trace, it's harder to help you, though.
Edit: Looking at Rails' caching code, I think this might be a deeper issue; it's attempting to get the length of output_buffer, which isn't going to be available outside of your views' contexts (that is, within Draper). You might try adding:
def output_buffer
h.output_buffer
end
But without testing it, I'm thinking it might not work exactly as planned without some more work. This is just a rough guess - I'd be surprised if this is actually the issue, but hopefully it gets you on the right path.
The note in the source there:
# VIEW TODO: Make #capture usable outside of ERB
# This dance is needed because Builder can't use capture
indicates that this isn't a fully-solved problem, so you may need to do a little digging around in the Rails internals to make this one work.
This works:
include Haml::Helpers
def another_way_to_try
self.init_haml_helpers
buffer = haml_buffer.buffer
h.with_output_buffer(buffer) do
h.cache "some_key10", :expires_in => 10.seconds do
h.concat "i should still not be empty 2"
end
end
end

What's difference in ::ModuleName::ClassName and ModuleName::ClassName

In ruby, I'm starting to see a pretty normal practice including modules and mixins referenced as ::ModuleName::ClassName, where in the past it was pretty much just ModuleName::ClassName.
What I'd like to get here is a decent understanding of why this practice is being seen more lately and what it does differently.
What's the difference?
What's the benefit (if the prior doesn't answer this)?
Thanks in advance for your input.
If you put the :: in the beginning you are referring to the global namespace, if you don't you are referring to your current namespace.
Usually if you don't have a class/module with the same name inside your class/module you would not need to use the :: in the beginning.
class Customer
def to_s
"Customer global"
end
end
class Order
class Customer
def to_s
"Customer within order"
end
end
def initialize
puts Customer.new
puts ::Customer.new
end
end
Order.new
will print out
Customer within order
Customer global
When you do ::ModuleName::ClassName you're saying:
I want you to look for ::ModuleName::ClassName at the root namespace, ignoring if this code is found inside another module. So, it will always look for a class that's named as ::ModuleName::ClassName and nothing else
When you say it like this ModuleName::ClassName, you're saying:
I want you to look for ModuleName::ClassName but looking at the current scope first and then at the other scopes. So, if you have a module called MyModule and this my module references ModuleName::ClassName then first try to find MyModule::ModuleName::ClassName then try to resolve ::ModuleName::ClassName.
When I'm defining code like this I almost always use ::ModuleName::ClassName to avoid any naming conflicts.

Resources