Ruby Class from .rb file - ruby-on-rails

I like to read ruby files from the filesystem and get the actual ruby class
Dir["app/controllers/admin/*.rb"].select{ |f|
require File.expand_path(f)
#how to turn 'f' into an actual class
}
The problem I have is that both Kernel.load or require just respond with a boolean. Is there a way to get the actual class. I know that I can use the file path to determine the name, but I like not to deal with namespaces. How can I do that?

First, I'm going to tell you up front that this is probably a bad idea. Files in Ruby have no relationship to classes whatsoever. A file can define one class, no classes, or many classes, and it can even define classes dynamically based on arbitrary conditions. Additionally, class definitions might be spread across multiple files, and classes can be altered dynamically at runtime. For this reason, determining reliably whether a class is defined in a file is a difficult task, to say the least.
That said, here's one way you might approach the problem. Note that this solution is very hacky, won't work in all cases, and it can load the same file more than once if you're not careful:
module ClassLoader
def self.load_classes(file)
context = Module.new
context.class_eval(File.read(file), file)
context.constants.map{|constant| [constant, context.const_get(constant)]}.to_h
end
end
Usage:
./test_file.rb:
if rand < 0.5
class A
end
else
class B
end
end
class C
end
Your code:
ClassLoader.load_classes('./test_file.rb') #=> {:A=>#<Module:0x9a3c128>::A, :C=>#<Module:0x9a3c128>::C}
Alternately, if you're using Rails class names can often be inferred from the file name. This is somewhat more dependable, since it relies on the same conventions that Rails does for autoloading constants:
Dir["app/controllers/admin/*.rb"].select{ |f|
File.basename(f).camelize.constantize
}

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

How do you store custom constants in Rails 4?

I made some regular expressions for email, bitmessage etc. and put them as constants to
#config/initializers/regexps.rb
REGEXP_EMAIL = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
REGEXP_BITMESSAGE = /\ABM-[a-zA-Z1-9&&[^OIl]]{32,34}\z/
and use it like
if #user.contact =~ REGEXP_EMAIL
elsif #user.contact =~ REGEXP_BITMESSAGE
Is that's good practice? What's the best way to store them?
It makes sense, that's one of the possible approaches. The only downside of this approach, is that the constants will pollute the global namespace.
The approach that I normally prefer is to define them inside the application namespace.
Assuming your application is called Fooapp, then you already have a Fooapp module defined by Rails (see config/application).
I normally create a fooapp.rb file inside lib like the following
module Fooapp
end
and I drop the constants inside. Also make sure to require it at the bottom of you application.rb file
require 'fooapp'
Lazy-loading of the file will not work in this case, because the Fooapp module is already defined.
When the number of constants become large enough, you can more them into a separate file, for example /lib/fooapp/constants.rb. This last step is just a trivial improvement to group all the constants into one simple place (I tend to use constants a lot to replace magic numbers or for optimization, despite Ruby 2.1 Frozen String literal improvements will probably let me remove several constants).
One more thing. In your case, if the regexp is specific to one model, you can store it inside the model itself and create a model method
class User
REGEXP_EMAIL = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
REGEXP_BITMESSAGE = /\ABM-[123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ]{32,34}\z/
def contact_is_email?
contact =~ REGEXP_EMAIL
end
end

Help resolving class and methods to reduce code

I have a method in the following folder:
/lib/app_name/my_file.rb
/lib/app_name/other_file.rb
In my code, things look like this currently:
APP_NAME::OTHER_FILE.some_method?(APP_NAME::MY_FILE::A::B)
is there a way I can include this once in my code so I don't have to reference it like this?
Particularly I want to access the method 'some_method' and the enumerations in the class MY_FILE.
In ruby everything is an object, even classes.
So you can assign the classes (or modules) to variables in order to create a local shorthand, like this:
other = APP_NAME::OTHER_FILE
mine = APP_NAME::MY_FILE::A::B
other.some_method? mine
You already shouldn't need to pass the names of any classes/modules under which you are currently namespaced, so for example, you can drop the APP_NAME:: portion in those class names.
If you want to reduce the size of the class names even further, you either need to literally change your class names (assuming you named them badly) or do as Pablo says and just alias them to shorter names. And yes, you can 'require' them to get the aliases in a single line:
Create a file, for example, the way it's done in gems: lib/app_name.rb (in the case of a Gem, lib/gem_name.rb). Inside that:
require 'app_name/my_file'
require 'app_name/other_file'
And inside other_file.rb and my_file.rb, create your aliases:
class AppName::MyFile
...
end
AppName::MF = AppName::MyFile
So now you can just require 'app_name' and all the classes will be loaded, along with their aliases.
Then you can reference AppName::MF instead of AppName::MyFile.
PS: You're using some weird naming convention, by the way ;) APP_NAME::MY_FILE as a class name is odd. Class names should be camel-cased.

Resources