How do I require classes with a circular dependency? [duplicate] - ruby

This question already has answers here:
Circular Dependencies in Ruby
(3 answers)
Closed 9 years ago.
I've been spoiled by Rail's autoloading of missing constants. In Ruby, if I have two classes, one nested inside the other but in different files, how do I require them since both depend on each other (circular dependency).
# user.rb
class User < ActiveRecord::Base
serialize :preferences, User::Preferences
end
# user/preferences.rb
class User::Preferences
end
# user_spec.rb
require 'user'
require 'user/preferences'
Note: I have not required the Rails environment.
If I try and load User first, the code fails because it does not know about User::Preferences yet. If I load "user/preferences" first, it fails when it loads User because the existing User class does not subclass ActiveRecord.
I have a suspicion I need to remove the circular dependency or, if possible, make serialize lazy load the class by passing a string 'User::Preferences' which is turned in to a constant when needed.

One hack I have is to create an empty User class inheriting from ActiveRecord::Base in user/preferences.rb:
class User < ActiveRecord::Base; end
class User::Preferences
end

Rather than wire knowledge of User's implementation into User::Preferences you could put the stub declaration in a common base, like so:
# user_base.rb
class User < ActiveRecord::Base; end
# user.rb
require 'user_base'
require 'user/preferences'
class User
serialize :preferences, User::Preferences
...
end
# user/preferences.rb
require 'user_base'
class User::Preferences
end
Alternatively, you could move User::Preferences into an independent module namespace such as ModelHelper::User::Preferences. I think I prefer this solution. The fact that you have a circular dependency is a code smell and the only thing causing it is the reuse of User class as a namespace container for User::Preferences.

Related

Chaining autoloaded classes in Ruby

I have a class, User in user.rb that will be autoloaded as needed by the following statement:
autoload :User, 'models/user.rb'
This model is shared between a few different codebases (as a Git submodule, if that makes a difference). In one such codebase, I have a need to reopen the User class and add some methods. Where this gets complicated, for me at least, is that I need the resultant, extended class to be autoloaded in place of the original class.
Is there a pattern for chaining autoloaded classes in Ruby? Something like:
autoload :User, ['models/user.rb', 'extended_models/user.rb']
Or should I be using inheritance instead of monkey-patching? I'm open to suggestions. Thanks in advance.
Here's what I ended up doing: my main file autoloads the extended class, then the extended class autoloads the base class. Reopening the class triggers the second autoload. Although a little clumsy, this lets me keep my base classes pristine while preserving autoloading behavior. (Autoloading is a requirement for me because the database isn't available for Sequel ORM to discover the table schemas when the app first fires up.)
It looks like this:
main.rb:
autoload :User, 'extended_models/user.rb'
extended_models/user.rb:
autoload :User, '../models/user.rb'
class User
def self.from_omniauth(authhash)
# ...
end
end
models/user.rb:
class User < Sequel::Model
# ...
end
I have some helper functions that help me autoload directories of models and single models with relative paths, but this is the general idea.
If its shared between several codebases, it would probably be the best to make it a Module instead of a class.

Rails: monkey-patching ActiveRecord::Base vs creating a Module

I am reading through The Rails 4 way (by Obie Fernandez), a well-known book about Rails, and from what I've read so far, I can highly recommend it.
However, there is an example section 9.2.7.1: Multiple Callback Methods in One Class that confuses me:
Bear with me, to make the problem clear for everyone, I have replicated the steps the book describes in this question.
The section talks about Active Record callbacks (before_create, before_update and so on), and that it is possible to create a class that handles multiple callbacks for you. The listed code is as follows:
class Auditor
def initialize(audit_log)
#audit_log = audit_log
end
def after_create(model)
#audit_log.created(model.inspect)
end
def after_update(model)
#audit_log.updated(model.inspect)
end
def after_destroy(model)
#audit_log.destroyed(model.inspect)
end
end
The book says then that to add this audit logging to an Active Record class, you would do the following:
class Account < ActiveRecord::Base
after_create Auditor.new(DEFAULT_AUDIT_LOG)
after_update Auditor.new(DEFAULT_AUDIT_LOG)
after_destroy Auditor.new(DEFAULT_AUDIT_LOG)
...
end
The book then notes that this code is very ugly, having to add three Auditors on three lines, and that it not DRY. It then goes ahead and tells us that to solve this problem, we should monkey-patch an acts_as_audited method into the Active Record::Base object, as follows:
(the book suggests putting this file in /lib/core_ext/active_record_base.rb)
class ActiveRecord::Base
def self.acts_as_audited(audit_log=DEFAULT_AUDIT_LOG)
auditor = Auditor.new(audit_log)
after_create auditor
after_update auditor
after_destroy auditor
end
end
which enables you to write the Account Model class as follows:
class Account < ActiveRecord::Base
acts_as_audited
...
end
Before reading the book, I have already made something similar that adds functionality to multiple Active Record models. The technique I used was to create a Module. To stay with the example, what I have done was similar to:
(I would put this file inside /app/models/auditable.rb)
module Auditable
def self.included(base)
#audit_log = base.audit_log || DEFAULT_AUDIT_LOG #The base class can override it if wanted, by specifying a self.audit_log before including this module
base.after_create audit_after_create
base.after_update audit_after_update
base.after_destroy audit_after_destroy
end
def audit_after_create
#audit_log.created(self.inspect)
end
def audit_after_update
#audit_log.updated(self.inspect)
end
def audit_after_destroy
#audit_log.destroyed(self.inspect)
end
end
Note that this file both replaces the Auditor and the monkey-patched ActiveRecord::Base method. The Account class would then look like:
class Account < ActiveRecord::Base
include Auditable
...
end
Now you've read both the way the book does it, and the way I would have done it in the past. My question: Which version is more sustainable in the long-term? I realize that this is a slightly opinionated question, just like everything about Rails, but to keep it answerable, I basically want to know:
Why would you want to monkey-patch ActiveRecord::Base directly, over creating and including a Module?
I would go for the module for a few reasons.
Its obvious; that is to say, I can quickly find the code that defines this behavior. In acts_as_* I don't know if its from some gem, library code, or defined within this class. There could be implications about it being overridden or piggy-backed in the call-stack.
Its portable. It uses method calls that are commonly defined in libraries that define callbacks. You could conceivably distribute and use this library in non-active-record objects.
It avoids the addition of unnecessary code on the static level. I'm a fan of having less code to manage (less code to break). I like using Ruby's niceties without doing to much to force it to be "nicer" than it already it is.
In a monkey-patch setting you are tying the code to a class or module that could go away and there are scenarios where it would fail silently until your class can't call acts_as_*.
One downfall of the portability argument is the testing argument. In which case I would say you can write your code to protect against portability, or fail early with smart warnings about what will and won't work when used portably.

Multiple inheritance in Ruby

Multiple inheritance in Ruby is simulated by including modules, but it's not possible to inherit properties directly from modules (that are not classes). The solution I came up was to define properties on the module initialization (code below). Is there a better way to achieve multiple inheritance compared the code below (inheriting methods AND properties)?
module MyCustomMixin
attr_accessor :a
def initialize
self.a = 1
end
def b
"something"
end
end
class MyCreateView < CreateView
include MyCustomMixin
end
class MyReadView < ReadView
include MyCustomMixin
end
class MyUpdateView < UpdateView
include MyCustomMixin
end
class MyDeleteView < DeleteView
include MyCustomMixin
end
The thing is, this is technically doable, but it requires a bit of finagling that doesn't look very pretty (especially with #initialize) - and rightfully so. I wouldn't recommend writing code in this way if the only purpose is to prevent code repetition.
So, consider:
What's the reason for having a MyFooView version for every FooView? Is it just to include mixins?
Perhaps all the Views have a common parent to which you could add this mixin?
Does the mixin contain functionality that is used in things other than View? Why not just add it directly to the parent View?
If the mixin is really that independent of the View classes, why isn't it just a class in itself, so that each View would own an instance of it?

Rails equivalent of ApplicationController for models

I understand that application_controller.rb is the place to put all the methods, etc that you would like made available in all your controllers since they all inherit from this class. Great.
But what is the equivalent for Models? In other words, I want a place where I can create a couple of super classes that my models will inherit from.
For example, I have a method that searches different tables for entries in all CAPS via REGEXP in Mysql. I'd like to be able to create the method only once and call it for different tables/models.
What is the Rails way of doing this?
I thought I could create a class that would inherit from ActiveRecord::Base (as all models do) , put the methods in there and then inherit all my models from that class. But thought there would surely be a better way to do it.
Thanks.
Edit
Per Semyon's answer I'm editing the post to show the routes I am using. It works now:
# models/dvd.rb
require 'ModelFunctions'
class Dvd < ActiveRecord::Base
extend ModelFunctions
...
end
# lib/ModelFunctions.rb
module ModelFunctions
def detect_uppercase(object)
case object
...
where("(#{field} COLLATE utf8_bin) REGEXP '^[\w[:upper:]]{5,}' ").not_locked.reorder("LENGTH(#{field}), #{table}.#{field} ASC")
end
end
In config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
Take a look at mixins, for example here:
http://ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
In a Rails app you could create a module in the lib directory that defines your methods and then include it in your models.
EDIT: To be specific for your example, you're trying to define a class method. You can do this in a mixin like this:
module Common
module ClassMethods
def detect_uppercase(object)
case object
when 'dvd'
field = 'title'
...
end
where("(#{field} COLLATE utf8_bin) REGEXP '^[\w[:upper:]] {5,}').not_locked.reorder('LENGTH(title), title ASC')"
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
Now when you include Common in your model, that model's class will be extended to include the new class methods, and you should be able to call Dvd.detect_uppercase.
Put the reusable method in some module next to your Dvd class. You can move it in a separate file later.
# app/models/dvd.rb
module CaseInsensitiveSearch
def case_insensitive_search(field, value)
# searching field for value goes here
end
end
class Dvd
end
After extending a class with the module you can use case_insensitive_search on the class. Including the module will make case_insensitive_search an instance method which is not what you want.
class Dvd
extend CaseInsensitiveSearch
end
Dvd.case_insensitive_search("title", "foo")
And of course you can use it inside Dvd class.
class Dvd
def self.search(query)
case_insensitive_search("title", query)
end
end
Dvd.search("foo")
Now when you made sure it works, you will probably want to move it in a separate file and use it across multiple classes. Place it in lib/case_insensitive_search.rb and make sure you have this line in config/application.rb:
config.autoload_paths += %W(#{config.root}/lib)
Now you can require it anywhere you want to use it:
require 'case_insensitive_search'
class Dvd
extend CaseInsensitiveSearch
end
The last thing I'd like to suggest. Create multiple modules with meaningful names. So instead of CommonModel have CaseInsensitiveSearch and so on.

Ruby: Raise error in Module if class method not found

I would like to put some code in module that throws an error if certain method is not defined.
This module relies on the external definition of this method, since this method's implementation is different for all classes. This code would help developers know early that they forgot to implement the method rather than when they tried to use features of the module.
module MyModule
def self.included(klass)
raise "MyModule: please `def my_method` on #{klass}" unless klass.respond_to?(:my_method)
end
end
I can easily raise an error in a module's included definition if a method is not defined, however since most modules are included at the top of a file, it's likely that my required method is defined in the class, but not before my module is included.
class MyClass
include MyModule
def self.my_method
# ...
end
end
This would still raise an error :(
Is it possible to raise an error only if the method truly is not defined in the class definition? Almost need a class.onload callback of sorts. If not, any other ideas for how to mitigate the possibilities that a programmer might include our module without defining this needed method?
Sounds like you want to make use of method_missing and define_method.
If you do use method_missing don't forget to:
call super for unhandled cases.
also implement a respond_to? method
look at this question, plus this and that.
Update:
It sounds the goal is to do static method checking like Java or c++ does. This is not really meaningful in ruby :-(
Since in ruby:
Each instance of an object has its own eigenclass. A given object may have the necessary methods mixed in at runtime. So just because Foo does not have a method at class load time is meaningless.
Frameworks like RoR hooks method_missing and dynamically create methods needed for the database query methods, so the method may exist (or not) when it is needed.
With regards to "class on load": A class definition is really executed. Try this:
class Foo
p "Hi"
end
You will see "Hi" the first and only the first time Foo is used. This is how things like devise hook into do their magic.
class User < ActiveRecord::Base
# **CALL 'devise' method**
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
# **CALL attr_accessible method**
attr_accessible :email, :password, :password_confirmation
end
So maybe by private convention have developers add a check_class method call to the bottom of the classes in question?
I understand the intent but it seems like fighting the way ruby is designed to work.
As a mostly Java person I appreciate the frustration. Let me guess: repeated cases of code getting pushed to production that had missing methods? :-P
Update2:
wrt onload In ruby barring use of frozen a class get new methods defined all the time. ( Or an instance can get new methods defined just for that instance. ) so checking for a method's nonexistence is only a snapshot check and not as definitive a check as a static language brings to the table. This is ruby's very own Halting problem
How about declaring a method with that name, which just raises an error, to make sure the user redefines the method?
module MyModule
def my_method
raise "Please implement me"
end
end
class MyClass
include MyModule
def my_method
# do something
end
end
Assuming your program requires all files when started and does not use any autoload and the like, you could use something like the following right after everything is required, but before the program actually starts:
classes_to_check = Object.constants.find_all do |const|
klass = Object.const_get(c)
klass.ancestors.include?(MyModule) if klass.kind_of?(Module)
end
classes_to_check.each do |klass|
raise "MyModule: please `def my_method` on #{klass}" \
unless klass.respond_to?(:my_method)
end
However, I personally always use Dogbert's solution.

Resources