Ruby on Rails Activemodel with Associations - ruby-on-rails

Acutally i face some hard exercises in computer science (hard for me i think, haha).
We're doing some basic stuff with Ruby on Rails an i have to open a csv file to get additional information on my 'User' model which is a normal rails scaffold.
So at the moment i open the csv file in my users_controller.rb file and search for the right row an add them to an instance variable.
But i wonder if i can write a class that acts like an ActiveRecord Model. So i change the code to use ActiveModel. But as i read in some google results, ActiveModel can't make use of ActiveRecord like associations. But it would great to have them.
So i hope you can help me. How can i provide my model with ActiveRecors like associations?
Greetings
Melanie

It's absolutely right that the CSV file should be represented as a model, as it's data.
However, trying to incorporate Active Model sounds tricky and would almost certainly require a great deal of hacking or monkey patching.
Unless you really need associations to other models, I would create a standalone class (i.e. not inheriting from ActiveRecord::Base) in the models directory, and put the logic for parsing the CSV in there:
class User
attr_accessor :name, :email, ...
def initialize(name,email,...)
# set data
end
def self.find(param_for_search)
# Parse CSV file, find line you want
# return a User instance
self.new(name, email)
end
end
I don't know exactly how your system works, but this way you can make it behave in a similar way to Active Model stuff. You can add similar class methods and each instance method represents a CSV file row.

Every time , when you are creating your own model , it is inheritance of ActiveRecord :
class Project < ActiveRecord::Base
attr_accessible :content, :name, :user
end
Then you can tell your model to have many (let's say) Project's Tasks , which creates an association . Please , provide an example of your app's logic.
Here is a quote from RailsCasts.com :
"In Rails 3 the non-database functionality of Active Record is extracted out into Active Model. This allows you to cleanly add validations and other features to tableless models."
There is also a nice description how to add functionality in you model by adding modules .

I understand, that using ActiveRecord to use an non database source is difficult, but i think it would be vewy charming if i could write something like this:
user.worktimes.first.value
in my view and get the information like it is a database table. I visit railscast.com an i found a episode where this ist discussed. But i would like to digg deeper in this. Are there any further ressources i could read?
As i understand, ActiveModel does not support associations? I wonder why associations wasn't moved to ActiveModel as it is a very useful thing. :)
So here is my code, that i was working on:
User-Model:
class User < ActiveRecord::Base
attr_accessible :department_id, :name
belongs_to :department
end
Department-Model:
class Department < ActiveRecord::Base
attr_accessible :name
has_many :users
end
And here is my CSV Model, that i created:
class Worktime
attr_accessor :user_id,:date,:value
def initialize(params)
dir = Rails.root.join('app', 'models', 'worktimes.csv').to_s
source = File.open(dir,'r')
while(line=source.gets)
data = line.split(';')
if data[0] = params[:user_id] && data[1] = params[:date]
#value = data[2]
end
end
end
end
I am very thankful for your help as its my first time using rails.

Related

Override gem behaviour

I'm using the acts_as_bookable gem for some basic reservation/booking stuff in a Rails app, and I need to add an additional validation to the Booking model that the gem creates.
What I mean by that is, inside the gem, located at lib/acts_as_bookable/booking.rb is the following module/class:
module ActsAsBookable
class Booking < ::ActiveRecord::Base
self.table_name = 'acts_as_bookable_bookings'
belongs_to :bookable, polymorphic: true
belongs_to :booker, polymorphic: true
validates_presence_of :bookable
validates_presence_of :booker
validate :bookable_must_be_bookable,
:booker_must_be_booker
# A bunch of other stuff
end
end
Which is fine. However, I want to add an additional piece of logic that stops a booker from booking the same instance of a bookable. Basically, a new validator.
I thought I could just add a file in my /models directory called acts_as_bookable.rb and just modify the class like this:
module ActsAsBookable
class Booking
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
But this doesn't work. I could modify the gem itself (I've already forked it to bring a few dependencies up to date, since it's a pretty old gem) but that doesn't feel like the right solution. This is logic specific to this app's implementation, and so my gut feeling is that it belongs in an override inside this specific project, not the base gem.
What am I doing wrong here? And is there a better/alternative approach that would be more suitable?
A clean way to create monkeypatches/augmentations to objects outside of your control is to create a seperate module:
module BookingMonkeyPatch
extend ActiveSupport::Concern
included do
validates_uniqueness_of :booker, scope: [:time, :bookable]
end
end
This lets you test the monkeypatch seperately - and you can "turn the monkeypatch on" by including the module:
ActsAsBookable::Booking.include(BookingMonkeyPatch)
This can be done in an initializer or anywhere else in the lifecycle.
Altough if bookable is a polymorpic assocation you need to use:
validates_uniqueness_of :booker_id, scope: [:time, :bookable_id, :bookable_type]
The uniqueness validation does not work correctly when just passed the name of an assocation as it creates a query based on database columns. This is an example of a leaky abstraction.
See:
Justin Weiss - 3 Ways to Monkey-Patch Without Making a Mess

Is this a known Rails Manager pattern

I am going through a co-workers code and am not able to find a single tutorial where this has been used. Can someone point me to some resources where this has been used. This has made code very clean but I haven't found any reference to it. This is only part of this class. It includes other some more methods.
class Manager
include ActiveModel::Model
include ActiveModel::Associations
attr_accessor :application_id, :user_id, :user_application_id,.........
belongs_to :application
belongs_to :user_application
belongs_to :user .. more belongs .......
# This method is necessary to enable this ActiveModel Class to be used in views along with Form helpers
def self._reflect_on_association(association) #:nodoc:
_reflections[association.to_sym]
end
def []=(attr, value)
self.send("#{attr}=", value)
end
def [](attr)
multi_attribute_ids = [:some_ids.to_s, :someid2.to_s]
return if multi_attribute_ids.include?(attr)
self.send(attr)
end
def applicant_name
end
-- some more methods
end
What would be the use of such a "manager". What are the two methods that are using self.send doing here. Is this a common pattern in rails.
Yes, with the introduction of ActiveModel in Rails 3, it has become an increasingly common pattern to use domain objects (called a manager in this case) that are not backed by an actual database table but which look and feel like models.
Even though ActiveModel makes it particularly convenient to pick and choose Rails model features to be incorporated into arbitrary classes, this pattern is something Rails pioneers have been encouraging since a long time.
As has been illustrated clearly in the example you posted, this pattern allows us to define virtual models and virtual associations which can easily take advantage of form helpers and other rails niceties written assuming model objects.

how to avoid repetition with identical models in rails

In my project I have two models which have identical attributes, methods and everything the same.
at the moment they are in app/models/ in separate rb files, with quite some repetition of code.
I would like to export all that code to a separate file and have the two files referring to it, and have mode DRY code.
I've tried the following, but it didn't work:
# app/models/order.rb
class Order < ActiveRecord::Base
before_save { self.version += 1 }
attr_accessible :order
attr_accessible :filled_date
validates :order, :presence => true
end
and one of the referring orders is:
# app/models/real_order.rb
class RealOrder < Order
belongs_to :User, inverse_of: :real_orders
end
but this doesn't work, and I get a Could not find table 'orders' when I try to use the models.
Also I'm thinking that Orders is not a real model, so probably the app/models is not really the right place for that file, though I'm not sure in what directory it should be.
thanks,
UPD1:
The structure I would like to achieve in the end is a situation where I have two identical database tables, with two separate models that are based on the same code. I would like to write such code only once in a separate superclass file. So I'm looking for DRY code, not DRY database.
There are a few different ways to share code between models. If it makes sense (for your problem domain) to use inheritance (as in your example above), then you need the following in your Order class:
self.abstract_class = true
You can also use mixins.
Here's a good question about this: ruby inheritance vs mixins

rails: create scaffold for models to inherit from superclass?

I'm new to Rails, still getting my feet wet, so please pardon me if this is either trivial or "the wrong way" to do things.
I'd like to create a superclass for some scaffolded models. For example, I'd like to create a scaffold for Men and for Women, but I want them both to inherit from a People superclass; Men and Women would inherit fields like height and weight from the People class.
Where/how do I define this People superclass? How do I define the subclasses Men and Women via scaffolding?
Usually I do something like:
rails g scaffold People type:string name:string birth:date height:integer
class People < ActiveRecord::Base
end
Important use the reserved word 'type'! That's where the table will keep which type the class is. Run the migration.
So, for the the subclasses you can do:
rails g scaffold Men --parent=People
resulting Men:
class Men < People
end
Same for Women:
rails g scaffold Women --parent=People
Resulting
class Women < People
end
No migration will be generated for the subclasses.
I'm not sure but this approach only works for STI.
Hope it, helps!
This is something I've thought about doing with my application. I haven't done it yet, and I wouldn't recommend it if you are new to rails. I would either make separate models entirely, or make one model, and have the attribute gender, which should be either a 0 or a 1, and then make a method that returns the string for the corresponding gender.
EDIT
So I opened up the rails console, and from what I could see, it is possible totally possible, all you need to do is declare the class, and if you want to use different tables, set_table_name
class This < That
set_table_name :this
end
class There < This
set_table_name :there
end
Or you could use one table, but if your trying to stay DRY, I would use two.
If you want to use the scaffold generator, you will have to run the typical rails g scaffold Men for each class you want views for (men and women). The model that this generates inherits from the ActiveRecord::Base class. The inheritance marker is the less than symbol (<).
# THESE WILL BE THE DEFAULT GENERATED MODELS
class Men < ActiveRecord::Base
end
class Women < ActiveRecord::Base
end
You will then manually create the super class User
class User < ActiveRecord::Base
end
and then edit the Men and Women models to inherit from User
# men.rb
class Men < User
end
# women.rb
class Women < User
end
lets say you wanted to subclass with one table, you could would right the migrations for that table, and then add the attr_accessible to the appropriate subclass.
attr_accessible is a rails security feature. It determines which attributes may be set in mass assignment. Anything related to security, site rank, etc. should not be accessible.
Example:
attr_accessible :favorite_food, :interests, :password, :email # THIS IS GOOD
attr_accessible :admin, :has_access_to_missile_launch_codes # THIS IS BAD
because then someone could undermine your security system by passing
params => { :man => { :admin => true }}
The main point is that using these attr_accessible will determine which type of user can set what. Obviously you can DRY this up by putting shared features in the super-class. Hope this helps
You should also read about the super keyword, and the self keyword. If your running an inherited setup you will eventually want to use these.
AFAIK you'd need to tweak the existing scaffolding templates, I don't believe there's a means to specify the controller base class. That said, I think in Rails 3 you can copy the templates into $ROOT/lib/templates/rails/... where ... depends on which you want to change.
That said, what's the real goal in doing this in a scaffold? In general, models will (a) only rarely be subclasses, and (b) even more rarely be the same subclass.
Just edit them by hand.
watch this screencast on single table inheritance.
http://railscasts.com/episodes/394-sti-and-polymorphic-associations
Single table inheritance and where to use it in Rails

Rails inheritance for classes of same behaviour but different attributes

I have been researching on the best approach for my problem which I originally had implemented as a single table inheritance but am deeply concerned about the scalability, as potentially will have thousands of columns in the table.
So the problem is I would like to have products which the methods of each are exactly the same the only difference being the attributes each one contains. It seems that in this situation that mutli-class inheritance (not supported natively in rails?) would be the best approach or some sort of polymorphic associations.
I want to work towards the following
#product.rb
Class Product < ActiveRecord::Base
attr_accessible :title .....
def to_s # some arbitrary method used by all extending classes
....
end
end
#book.rb
class Book < Product
attr_accessible :author...
end
So I want the book to inherit the methods from product and not for the product to know about the attributes required by each subclass. And if possible get all of the products through one query.
I need to know the best way of approaching this, and if I am doing it completely wrong, please note the code written above is just for example to simplify my problem.
What you can do is create a module and include it in several different models.
First, create a file in your lib directory
i.e.) my_module.rb
module MyModule
def full_name
"#{first_name} #{last_name}"
end
end
Then, make sure the module is loaded when your Rails App starts:
In config/application.rb:
config.autoload_paths += %W(#{config.root}/lib)
Finally, include it in your models:
i.e.) app/models/thing.rb
class Thing < ActiveRecord::Base
attr_accessible :first_name, :last_name
include AdditionMod
end
You can test it in the console:
#thing = Thing.create(first_name: "Awesome", last_name: "Module")
#thing.full_name
=> "Awesome Module"
Found out that I can use H-store in conjunction with postgres that allows me to have a column that contains a schema less hash that can be used with the power of postgres (for an example take a look at http://hstoredemo.herokuapp.com/)

Resources