Ruby module as both namespace and mixin - ruby-on-rails

In the Rails application I’m currently developing I have many “request” models. We are using a lot of web services and we have a lot of different requests to those services, each of them with their own logic (mostly validations). So they are all grouped in a module as a namespace:
module Request
end
So now every request is something like:
class Request::SendSomeData
end
So far, so good… The thing is that we are going to have a lot of such requests which will share some common logic. It is easy to include the module
class Request::SendSomeData
include Request
end
… (so it will act both as a namespace and a mixin), but I was wondering if there is a way to make it without the include (as it is going to be some kind of code repetition).
Is there a way for Ruby to put some instance methods to all the classes in a module’s namespace without explicitly including the module?
In other words can I have something like:
module Request
def someMethod
end
end
and
class Request::SendSomeData
end
and be able to use
Request::SendSomeData.new.someMethod
at the same time?

What you are asking can't be done without some evil hackery.
You either have too many classes, or are focusing on an issue that is too unimportant. It's only one line of code per class.
An alternative is to create a base model class, as I have previously described in another question, but this requires the derived classes to each call set_table_name, so it won't save you any typing.

Related

Are Concerns in Ruby on Rails replacement for service classes in MVC?

I am a newbie in Ruby on Rails. Coming from a C# and Java background, Ruby on Rails seems bizzare, yet interesting at the same time. It's almost like coming from a class-based object-oriented world to the prototyping concept of JavaScript, or even to a functional language.
Anyways, in a traditional C# or Java MVC application, I tend to keep my models and controllers as clean as possible, extracting the business logic into service classes. My models are just POCOs/POJOs (with some calculated fields and validation at most). And my controllers just process incoming requests (relying heavily on dependency injection) and then return a view or JSON.
Yet, I do not see any clear pattern in the RoR world. Some people tend to put all their business logic into controllers, some put it into models (with ActiveRecords, it kind of makes sense, although I don't like it).
And then there is the concept of Concerns. Are they the right place to extract my business logic, instead of using services? If yes, could you include an example of proper Concers use? I still struggle with the concept of modules (are they more of namespaces, or rather interfaces)? As said at the beginning, Ruby seems like a whole new galaxy to me.
This question could get into the weeds a bit as it brings in a lot of personal preferences. However here is my take on it.
First, Concerns are not a replacement for Service classes. Concerns are a clean and nifty way to manage your mix-ins. If you are new to Ruby, mix-ins are basically a way of injecting instance and/or class methods into existing classes. For example, given these classes:
class EvilRobot < ActiveRecord::Base
def destroy(target)
...
end
end
class OrneryTeenAger < ActiveRecord::Base
def destroy(target)
...
end
end
you could dry out the code with:
require 'active_support/concern'
module EvilTools
extend ActiveSupport::Concern
included do
def destroy(target)
...
end
end
end
class EvilRobot < ActiveRecord::Base
include EvilTools
end
class OrneryTeenAger < ActiveRecord::Base
include EvilTools
end
I think that the vast majority of Rails developers, myself included, go for a fat-model, thin controller design. Just how fat though is a matter of taste. I also tend to move functionality to classes under lib if they don't fit logically within a model, or extract into an engine or gem.
I would say jpgeek reaction is part of the answer. The is a lot of movement towards service objects to clean up fat models or large controllers actions. Just create an app/services folder and create service classes like:
class TargetDestructionService
def initialize(shooter, target)
#shooter = shooter
#target = target
end
def execute
#Lot of code that causes the destruction of the target.
end
end
Then in your model or controller you would call:
TargetDestructionService.new(EvilRobot.new, Human.new).execute
Here is a nice article about it: https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services

Where to properly outsource model code in rails 4?

In a model of mine I'm using the bing translator gem for automatic translation of a model attribute via a after_create callback:
class Place < ActiveRecord::Base
after_create :auto_translate
....
# AUTO_TRANSLATE STUFF
def initialize_bing_translator(bing_id, bing_secret)
t = BingTranslator.new(bing_id, bing_secret)
<do other stuff>
end
def auto_translate
<do stuff>
end
<further auto_translate methods>
The whole bunch of functions seems to bloat the model code a little so I want to put it into some extra module. Where exactly shall I place the .rb-file? Is this a use-case for a concern (a concept I did not fully understood)? Is it better to define a seperate module in the model file itself or to place it in /lib/user_modules/? Is there sth like a rule-of-thumb? The information available on the web are confusing me a little and I'd be glad if someone could shed some light on that issue for me!
I started putting stuff like that in subfolders of models and namespacing things accordingly. In your specific case I'd put it in something like models/place/translator.rb (or similar) and call the class Place::Translator.
If you just want a sort of logical separation but will not use the same functionality elsewhere you can use concerning instead of a separate concern.
There are many options and most of them are primarily opinion-based. It depends on if you plan to reuse that class, how complex your application already is and if the new class has any external dependency.
I would think about: create your own gem, add to app/models, add a new app/translators directory, perhaps in lib...

Restructure bulky class

I have an ActiveRecord model Reservation.
It got to the point that the class is to large and does too much.
I would like to split it into a few different ones and place those under the Reservation module.
Unfortunately this will break the app.
At this moment I see following options:
namespace it to something like ReservationConcerns or similar
add the functionality to the Reservation class itself, but physically move it to the subdir (Reservation would be in app/models/reservation.rb, Reservation::Pipeline would be in app/models/reservation/pipeline.rb etc).
So the question is how to structure the different concerns of a feature already having it as one single, bulky class without breaking the app.
If you want to split up a Ruby class into different components without changing its public interface, one solution is to use modules:
# app/models/reservation.rb
class Reservation < ActiveRecord::Base
# associations, validations, etc.
include Pipeline
end
# app/models/reservation/pipeline.rb
module Reservation::Pipeline
def some_pipeline_method
# ...
end
def other_pipeline_method
# ...
end
end
ActiveRecord also provides observers, which "is a great way to reduce the clutter that normally comes when the model class is burdened with functionality that doesn't pertain to the core responsibility of the class". Observers often make heavy use of the ActiveModel::Dirty methods.
These suggestions are complimentary: modules can help you group your interface into more local chunks, while observers can make backend details more self-contained. From here, it's difficult to be more specific without knowing exactly what pieces you have that you're trying to break out.

Are modules in ruby only meant to be used when multiple objects require its functionality?

I've read that the main service provided by a module is to contain and group similar functions that are needed by several objects. But is it a good practice to create a module for something like a parser even if you are certain that only one object will ever require its services?
Modules serve two purposes. The first as you have noted is to allow functionality to be added to a diverse set of classes (diverse meaning outside the inheritance tree). The second an by far more common use is to organize the names space. When you see code like ActiveRecord::Base or BSON::ObjectID the authors of these gems have placed their functionality in a module, ActiveRecord and BSON respectively, that prevents their class names from conflicting with the applications they are included in or other libraries.
With the parser you mentioned, it sounds to me like you want to construct a singleton class rather then a module.
It can help for organization to create a module even if you are using it in one place. For example you might have a lot of methods that are all related, but not really related to the code you are including the module in. This is similar to the the organization that a class provides, except that a module isn't focused on an object.
main service provided by a module is
to contain and group similar functions
that are needed by several objects
I would re-word it to "group similar functions that might be needed by at least one object". It's very frequent that you don't know how many classes will end up including or extending a module (for example if you are writing a library)
Independently of that, I see two more "main purposes":
They are a way to provide scopes (you can package several classes and other modules inside one module).
They have a callback called included. This might seem trivial, but that callback is key in a lot of interesting meta-programming techniques.
I would go for a class (if you got not too much code). Place it under /lib
class Parse
def self.xml(xml_string)
...
end
end
Parse.xml("<xml></xml>")
You can call it from anywhere.
http://en.wikipedia.org/wiki/Separation_of_concerns
Modules are indeed a group of similar functions. If you got a lot of parsing code you could do:
class Xml
acts_as_parser
end
class Json
acts_as_parser
end
...
acts_as_parser will load the group of functions from the module.
But is it a good practice to create a module for something like a parser even if you are certain that only one object will ever require its services?
Yes.
In Ruby, modules help a whole lot with separation of concerns and are very good for doing "aspect-oriented" programming (i.e., fun with mixins).
# Basic Hierarchy
class X
def say ; 'X' end
end
class Y < X
def say ; super + 'Y' end
end
y = Y.new
y.say #=> 'XY'
# Throwing in an aspects/mixins
module A
def say ; super + 'A' end
end
class Y
include A
end
y.say #=> 'XAY'
# Throwing in another aspects/mixins
module B
def say ; super + 'B' end
end
class Y
include B
end
y.say #=> 'XABY'
When faced with an excessively large class, sometimes I split it up into a couple of modules based on its functionality, and have a virtually empty class just include those modules.

Ruby is already using the class name of my model

I'm making a forum application with various levels of authorization, one of which is a Monitor. I am doing this by extending my User class, and I plan on fine tuning this with "-ship" classes (e.g. administratorship, authorship, moderatorship, etc.). Apparently the Monitor class is part of ruby mixin. How do I keep my resource name without the collisions?
Some possibilities:
avoid the require 'monitor.rb' call which is pulling in the standard Monitor instance
do some runtime magic to rename the existing Monitor class.
monkey with your load path so that require 'monitor.rb' pulls in an empty implementation of Monitor.
But in all cases you could end up with the situation where a 3rd party library is using Monitor expecting it to be the standard Monitor class. So, I'd advise against any of the above.
I'd say your only two reasonable options are:
A) you could put your class in a namespace:
Module MyApp
class Monitor
#...
end
end
if your app uses some kind of auto-require magic (e.g it's a rails app) then you would put your implementation in /my_app/monitor.rb. When you wanted to refer to that class you would do something like my_monitor = MyApp::Monitor.new(), or whatever.
B) you could use a different class name :)
Declare your Monitor class in other module.
module MyModule
class Monitor
end
end
FYI, I just found a neat trick (errr, hack) to get around this which may work.
I work on a large legacy application which, unfortunately, has a "Fixture" model which is quite important and which is used everywhere. When running tests, it's impossible to create a Fixture instance because of the Fixture class used by ActiveRecord when running tests. So I did the following:
FixtureModel = Fixture.dup
This freezes my class in place so that I can refer to it later (but just in my tests!) without being extended by the ActiveRecord Fixture class (which is not namespaced)

Resources