Is my thinking about OOP persistence-separation right? - ruby-on-rails

I am reading about OOP and trying to apply it to Rails (inspired by Uncle Bob's Ruby conf talk), and I was wondering, is it right to access the repository from a "logic" model in the way I do below:
class Product
...
def pick_a_random_product
repository.pick_a_random_product
end
...
end
Does this qualify as "logic being separated from persistence" ? Is it O.K to be using the repository as extensively as may be needed, inside a "domain"/"logic" model?
repository is meant to be some class that does the real job of fetching something from the database.

It works but if pick_a_random_product is use for different class means that
Suppose you have class xyz
Class Xyz
...
def pick_a_random_product
Product.pick_a_random_product
# Xyz.pick_a_random_product you can't use this.
end
...
end
you have class Product
class Product
...
def pick_a_random_product
Xyz.pick_a_random_product
end
...
end

Related

Defining/nesting modules and classes in a Rails app

I'm trying to define a variety of modules/classes in a Rails app. My directory structure looks something like this:
lib/
fruit/ # just a module, with no associated file
fruit_operator.rb
apple.rb # abstract class, defines behavior for inheritance
orange.rb # abstract class, defines behavior for inheritance
apple/
granny_smith.rb # should inherit from apple.rb
red_delicious.rb
orange/
valencia.rb
seville.rb
I want two things:
The sub-classes should inherit from their parent classes (Apple and Orange).
I should be able to access these classes from a top-level (within /fruit file -- ie fruit_operator.rb
All the attempts I've tried to get this working are throwing an error of some sort or another.
Attempt # 1:
apple.rb
module Fruit
class Apple
def juicy
true
end
end
end
apple/granny_smith.rb
module Fruit
class GrannySmith::Apple
end
end
When I try to access GrannySmith from fruit_operator.rb I run into errors. Accessing as simply GrannySmith generates
uninitialized constant Fruit::FruitOperator::GrannySmith
If I try Fruit::GrannySmith, I get
uninitialized constant Fruit::GrannySmith
If I try Apple::GrannySmith or Fruit::Apple::GrannySmith, I hit the error
Unable to autoload constant Fruit::Apple::GrannySmith, expected /lib/fruit/apple/granny_smith.rb to define it
Attempt #2:
apple.rb
class Fruit::Apple
def juicy
true
end
end
apple/granny_smith.rb
class GrannySmith < Fruit::Apple
end
Attempting to access from fruit_operator.rb, I run into identical errors as the above.
Attempt #3:
apple.rb
class Fruit::Apple
def juicy
true
end
end
apple/granny_smith.rb
class Fruit::Apple::GrannySmith
end
This last version allows me to access the class directly from fruit_operator.rb (as Apple::GrannySmith), but it doesn't inherit from Apple!
Any idea how to structure/access these classes and modules? I've looked around quite a bit (on SO and elsewhere), and can't find a great guide for how to do this, particularly in a Rails app.
You must import the definition of the fruit files into the fruit operator file. For example,
require_relative './apple/granny_smith'
I think you're best solution is to implement Fruit as a class, and have Apple and Orange both inherit from Fruit, and GrannySmith inherit from Apple, like so:
Class Fruit
def seeds?
true
end
end
Class Apple < Fruit
def juicy
true
end
end
class GrannySmith < Apple
def color
"green"
end
end
Depending on what your need for the fruit_operator is, you may choose to include those methods/actions via a mixin Module.

Building dependent relationships in Rails - is there a better pattern than after_create?

I have a model that requires a ton of dependencies... it looks something like this:
after_create :create_dependencies
def create_dependencies
create_foo
create_bar
create_baz
# ...
end
This is really polluting my model class with a bunch of this nonsense. Is there another way I can go about this? I would love to use a form object or something like that, but I want to ensure all of these objects come with their dependent relationships no matter what, even when created through command line, in a test suite, etc.
My first reaction was to create a Form object like you mentioned (as described by Ryan Bates). However you're right that if you save a model directly none of the dependencies will be created.
One approach you could take is to refactor the dependency creation out into a separate class:
# lib/my_model_dependency_creator.rb
class MyModelDependencyCreator
def initialize(my_model)
#my_model = my_model
end
def create_dependencies
create_foo
create_bar
# etc
end
private
def create_foo
# create dependency associated with #my_model
end
def create_bar
end
end
Then in your model class:
...
after_create :create_dependencies
def create_dependencies
MyModelDependencyCreator.new(self).create_dependencies
end
First, any thought about observers?
Second, I guess it's not that hard to extract the code.
#include this module in your model
module AutoSaveDependency
def auto_save_dependencies *deps
##auto_save_dependencies = deps
end
def auto_save
##auto_save_dependencies.each {|dep| send "create_#{dep}" }
end
def self.included(model)
model.send :after_create, :auto_save
end
end
So in your model, you just include this module, and write auto_save_dependencies :foo, :bar, ...
It could be more complicated but I think it's doable

Converting inheritance to composition in rails

I have a model in a legacy system that looks something like this:
class Prize < ActiveRecord::Base
def win
# do a bunch of things
end
end
We started off with one prize, but like anything else the type of prizes we are dealing with is starting to expand. So now def win is doing a whole bunch of case/switching to decide prize type.
For that reason I decided to do this:
class DailyPrize < Prize
def win
#do only daily prize stuff, no type checking.
end
end
This code came under review before we sent it off to QA and now I am asked to do this using composition (mixin) and not subclassing. I cannot think of a clean way to do this.
The legacy code base is doing the following in a bunch of places and I did not want to go changing stuff all over the place:
prize = Prize.new
prize.win
So, my question how to make this happen using composition?
Here is what I understand by replacing your code by composition over inheritance.
class Prize < ActiveRecord::Base
def prize
#prize ||= PrizeFactory.build(self)
end
def win
prize.win
end
end
class PrizeFactory
def self.build(prize)
if prize.daily?
DailyPrize.new(prize)
# other condition to build specific prize
end
end
end
class DailyPrize
def initialize(prize)
#prize = prize
end
def win
#do only daily prize stuff
#access #prize to get #prize attribute
#if you use it, you have coupling (see below)
end
end
The thing is, this may not be better than your implementation, it really depend of what your are achieving in term of domain logic.
With composition, one goal is to reduce the coupling between object, if you are calling a lot of #prize object methods in the DailyPrice win method , you have a tight coupling between this two classes and you may lost the benefits of composition.
One way I can think of is having specific prizes as Modules, e.g.
module DailyPrize
def specific_method_1
end
def specific_method_2
end
end
... and then having the Prize class as :
class Prize
def win
# do something in common
specific_method_1
# do something in common
specific_method_2
# ...
end
end
And then you can mixin a module as you choose, for example when instantiating the class
def initialize (prize_type)
# mixin the appropriate module
end

How to put many Model classes together in one Module to use in Rails

In rails i have a lot of tables and need to define a lot of Model classes to use for controller, but i want to put all the model classes into one module file, and then make controller to use the model class in the module, but i don't know how to do it.
Could someone give me help on the problem? Appreciate your help very much.
app/models/widgets/blue_widget.rb
class Widgets::BlueWidget < ActiveRecord::Base
// etc.
end
app/controllers/blue_widget_controller.rb
def index
#widgets = Widgets::BlueWidget.all
end
You can also namespace the controllers.
Edit:
lib/widgets.rb
module Widgets
class BlueWidget
end
class RedWidget
end
end
controller:
require 'lib/widgets'
def index
#widgets = Widgets::BlueWidget.all
end
Is that what you mean?
You can also generate models directly in subdirectorys and get them im modules:
rails g model user/likes name:string like:boolean
and you will get your generated files.
The generated model would be in app/models/user/likes.rb
class User::Likes < ActiveRecord::Base
end

how to define/attach rails validations in multiple generations of modules

I've found a way to make this work, but am curious about a better way / the Rails 3 way. (I'm using 2.3.5 still, but hope to migrate around New Year's.)
The situation: I've got two layers of module inheritance, the second layer gets mixed into a Rails model. Both modules define validation methods and I'd like both of them to attach the validations to the base class, but because of the two levels of inheritance, the following doesn't work:
def self.included(base)
base.validate :yadda_yadda
end
When that module is included by another module, the interpreter grinds to a screeching halt because Modules don't know about ActiveRecord::Validations. Including the validations module begs the question of "where is save?" thanks to alias_method.
The following works, as long as you remember to call super whenever you override validate(). I don't trust myself or future maintainers to remember that, so I'd like to use the validate :yadda_yadda idiom instead, if possible.
module Grandpa
def validate
must_be_ok
end
def must_be_ok
errors.add_to_base("#{self} wasn't ok")
end
end
module Dad
include Grandpa
def validate
super
must_be_ok_too
end
def must_be_ok_too
errors.add_to_base("#{self} wasn't ok either")
end
end
class Kid < ActiveRecord::Base
include Dad
validate :must_be_ok_three
def must_be_ok_three
errors.add_to_base("#{self} wasn't ok furthermore")
end
end
Suggestions? Tips for Rails 3 approach? I don't think the validations API has changed that much.
I solved it (when I ran into the same problem, but with something other than validation).
Short answer: you can call send(:included, base) on the module you want to bring in. Within the higher-up included() definition, you need to check whether the base is a Class or a Module.
Why would you ever want to do this? Well, I've got some modules that extract some common functionality out of my models. For instance, the module HasAllocable sets up a polymorphic belongs_to relationship, and a getter/setter pair for a virtual attribute. Now I have another module that needs to pull in HasAllocable, to spare the base classes from having to remember it.
I'd be interested to know whether this smells funny to anyone. I haven't seen anything like it on the web, so I wonder if multiple layers of model inheritance is more of an antipattern.
module Grandpa
def self.included(base)
if base.kind_of?(Class)
base.validate :must_be_ok
end
end
end
module Dad
include Grandpa
def self.included(base)
if base.kind_of?(Class)
# you can do this
#base.send(:include, Grandpa)
# you can also do this
Grandpa.send(:included, base)
# this does not invoke Grandpa.included(Kid)
#super(base)
base.validate :must_be_ok_too
end
end
end
class Kid < ActiveRecord::Base
include Dad
validate :must_be_ok_three
end

Resources