Help resolving class and methods to reduce code - ruby-on-rails

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.

Related

Where to store abbreviations for long class names in Rails?

I constantly need to use long class name like
SomeReallyLongModuleName::AnotherLongModuleName::YetAnotherLongClassName
in the project. I want to access this class by something like YALCN. Where it's the best to keep definition like
YALCN = SomeReallyLongModuleName::AnotherLongModuleName::YetAnotherLongClassName
And is it a common practice, or people constantly use fully-qualified long name?
If this is only for Rails console usage, I'd put them into /lib/aliases.rb or somewhere similar and require the file when needed.
You could make "local" aliases. Something like this:
class Another
YALCN = SomeReallyLongModuleName::AnotherLongModuleName::YetAnotherLongClassName
def do_something
YALCN.new # or whatever
end
end
To avoid problems with autoloading, I'd keep the short constants in the same place they're used.
And is it a common practice, or people constantly use fully-qualified long name?
I haven't seen this too often.

Rails - Call a method form a model or helper with an alias

I have a helper called GlobalHelper.
There are a couple of constants (e.g. GLOBAL_URL) and methods (e.g. self.get_url) in it.
I'm trying to make an alias or an equivalent of the macros in C (#define URL) to access the constant and methods from any of my views and controllers easily.
For now I have to do it like this:
GlobalHelper.get_url(GlobalHelper::URL_TYPE_PAGE, ["page1"])
And I want to get rid of the GlobalHelper everywhere with alias/macro to get a code like this instead:
aliasmethod(aliasconstant, ["page1"])
How can I do that?
ruby is not c, so you should take a look how to write ideomatic ruby code before trying to bring such concepts into your project.
since i don't know what exactly your get_url method does, i can only give limited recommendations.
one thing that would probably result in much nicer code is using symbols instead of constants:
GlobalHelper.get_url(:page, ["page1"])
when using a Module instead of Class you can include it and call the method directly:
include GlobalHelper
get_url(...)
these are just examples of what could be done. not saying that this is better than explicitly calling GlobalHelper directly. nothing wrong with that at all.

Ruby Class from .rb file

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
}

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

See where a symbol is defined in irb

I work on a pretty large rails project at work. Sometimes I need to hunt down class / constant definitions. Is there some built-in method in Ruby to do this for me? Example:
irb> SOME_CONSTANT.__file__
=> /some/path/to/a/file
This isn't exactly what you're looking for, but methods do have a .source_location method on them. You can use this to find out where a class is actually implemented. (Since ruby lets you reopen classes, this could be in multiple places)
for example, given an instance of an object, i:
i.methods.map do |method_name|
method_obj = i.method(method_name)
file, line = method_obj.source_location
file #map down to the file name
end.uniq
will give you a list of all the files where i's methods are implemented.
This will work for classes that have at least 1 method implemented in ruby. It won't work for constants, though.
At the very beginning before any file is loaded, insert a line that defines the class/constant that you want to check as something other than a module. For example, suppose you have class or other kind of constant A within your code, and want to know where it is defined. Then, at the very beginning of the main file, write
A = nil
Then, when the program is run, whenever it first meets the definition of class/constant A, it will show something like
some_path_to_a_file:line_number in `some_method': A is not a class (TypeError)
or
some_path_to_a_file:line_number: warning: already initialized constant A
Then, some_path_to_a_file:line_number will be the location where A is defined.
If you're using Ruby 1.9.2, #YenTheFirst's answer is correct: call #source_location on a Method object.
If you're using Ruby 1.8.7, then #source_location doesn't exist (yet). You'll need something like this implementation of a method. (There's another one or two floating around, but I can't find the other one real quick).

Resources