One-To-Many-Through adding join model instances - one-to-many

My models:
class Test
include DataMapper::Resource
property :id, Serial
property :name, String, :default => ''
has n, :test_visits
has n, :visits, :through => :test_visits
# ...
end
class Visit
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :test_visits
has n, :tests, :through => :test_visits
# ...
end
class TestVisit
include DataMapper::Resource
property :result, String
belongs_to :test, :key => true
belongs_to :visit, :key => true
end
Why this code raises an SaveFailureError?:
#visit.test_visits.clear
#results.each do |test, result|
#visit.test_visits.new(:test => test, :result => result)
end
#visit.save
where variable #results is Hash (keys: Test, values: String)

It raises error because child objects are not saved. Try this:
#results.each do |test, result|
TestVisit.create(:visit => #visit, :test => test, :result => result)
end

Related

DataMapper many-to-many association

At the moment I have the database model up and can populate the models with data:
user.persons << person
group.functions << function
group.classificationlevels << clasfication
user.groups << group
These are the models I am using at the moment for getting the data associated with each other:
module Core_authentication
class User
include DataMapper::Resource
property :id, Serial
property :username, String, :required => true, :unique => true
property :password, BCryptHash, :required => true
property :email, String, :format => :email_address, :required => true
property :created_at, DateTime
property :updated_at, DateTime
#Creating join tables to link to group and person information
has n, :persons, :through => Resource
has n, :groups, :through => Resource
def username= new_username
super new_username.downcase
end
end
class Group
include DataMapper::Resource
property :id, Serial
property :groupname, String, :required => true
#Another jointable link group to link to functions and classification levels
has n, :functions, :through => Resource
has n, :classificationlevels, :through => Resource
has n, :users, :through => Resource
def groupname= new_group
super new_group.downcase.capitalize!
end
end
class Person
include DataMapper::Resource
property :id, Serial
property :firstname, String
property :lastname, String, :required => true
property :adress, String
property :postcode, String, :length => 6, :required => true
property :telefoon, String
property :created_at, DateTime
property :updated_at, DateTime
has n, :users, :through => Resource
def firstname= new_firstname
super new_firstname.downcase.capitalize!
end
def lastname= new_lastname
super new_lastname
end
end
class Function
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :groups, :through => Resource
end
class Classificationlevel
include DataMapper::Resource
property :id, Serial
property :levelcode, Integer
property :name, String
has n, :groups, :through => Resource
end
end
I want to get a user's groups they are a member of, and the classification level that is associated with each group.
I tried multiple ways of doing this, and also looked around on the web, but I cannot find a clear explanation on how I must do this, and so I can't get it working.
The documentation for Datamapper (section for "Has, and belongs to, many (Or Many-To-Many)") has some hints, here's a simplified example of your models:
require 'sqlite3'
require 'dm-core'
require 'dm-sqlite-adapter'
require 'dm-migrations'
DataMapper.setup(:default, 'sqlite3:m2m.sqlite3')
class User
include DataMapper::Resource
property :id, Serial
has n, :persons, :through => Resource # => PersonUser
has n, :groups, :through => Resource # => GroupPerson
end
class Group
include DataMapper::Resource
property :id, Serial
has n, :functions, :through => Resource # => FunctionGroup
has n, :classificationlevels, :through => Resource # => GroupClassificationlevel
has n, :users, :through => Resource # => GroupUser
end
class Person
include DataMapper::Resource
property :id, Serial
has n, :users, :through => Resource # => PersonUser
end
class Function
include DataMapper::Resource
property :id, Serial
has n, :groups, :through => Resource # => FunctionGroup
end
class Classificationlevel
include DataMapper::Resource
property :id, Serial
has n, :groups, :through => Resource # => GroupClassificationlevel
end
And an example of using them (if you put both of these snippets in a file and run them, you can see the output):
DataMapper.finalize
DataMapper.auto_migrate!
user = User.create
group = Group.create
# link them by adding to the relationship
user.groups << group
user.save
p user.groups # => [#<Group #id=1>]

Options for validates_with

I'm not able to access the values, passed as option in 'validates_with'
My model:
class Person < ActiveRecord::Base
include ActiveModel::Validations
attr_accessible :name, :uid
validates :name, :presence => "true"
validates :uid, :presence => "true"
validates_with IdValidator, :attr => :uid
My Custom Validator:
Class IdValidator < ActiveModel::Validator
def validate(record)
puts options[:attr]
...
...
end
end
For testing purpose, I'm printing "options[:attr]" and all I see is ":uid" in the terminal and not the value in it. Please help!
When you pass in :attr => :uid, you're just passing in a symbol. There's no magic happening hereā€”it just takes the hash of options you've attached and delivers it as the options hash. So when you write it, you see the symbol you've passed.
What you probably want is
Class IdValidator < ActiveModel::Validator
def validate(record)
puts record.uid
...
...
end
end
Because validates_with is a class method, you can't get the values of an individual record in the options hash. If you are interested in a more DRY version, you could try something like:
class IdValidator < ActiveModel::Validator
def validate(record)
puts record[options[:field]]
end
end
class Person < ActiveRecord::Base
include ActiveModel::Validations
attr_accessible :name, :uid
validates :name, :presence => "true"
validates :uid, :presence => "true"
validates_with IdValidator, :field => :uid
end
Where you pass in the name of the field you want evaluated.

How to create ActiveModel Object in rails 3

I am having the following model class on ActiveRecord. How to write an equivalent ActiveModel for this class?
class Recommendation < ActiveRecord::Base
def self.columns() #columns ||= []; end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
column :from_email, :string
column :to_email, :string
column :article_id, :integer
column :message, :text
serialize :exception
validates_format_of :from_email, :to_email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :message, :maximum => 500
belongs_to :article
end
I suggest you start with a plain class, and then start adding in ActiveModel modules. Say, start with validation.
http://api.rubyonrails.org/classes/ActiveModel/Validations.html
class Recommendation
include ActiveModel::Validations
attr_accessor :from_email, :to_email, :article_id, :message
validates_format_of :from_email, :to_email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :message, :maximum => 500
end
The other ActiveModel docs can be found at http://api.rubyonrails.org/

Find and count all Profiles with object name of HABTM relationship

In my Rails 3 app I have two models, Profile and Item. Each has a HABTM relationship with the other. In my Profile model, I have a method wish_items that creates an Array of that same name (wish_items). If an item contains the category "wish", it is added to the wish_items Array for that profile.
For the purpose of this question, say I have an item named "phone" with category "wish". What I'd like to do is be able to find and count all Profiles that have "phone" in their wish_items Array so I can render that count in a view. My code is below.
My Profile.rb model:
class Profile < ActiveRecord::Base
has_and_belongs_to_many :items
def wish_items
wish_items = Array.new
items.each do |item|
if item.category == "wish"
wish_items << item
end
end
return wish_items
end
end
My Item.rb model:
class Item < ActiveRecord::Base
has_and_belongs_to_many :profiles
end
I have a join table items_profiles for this relationship. Here is that migration:
class CreateItemsProfiles < ActiveRecord::Migration
def self.up
create_table :items_profiles, :id =>false do |t|
t.references :item
t.references :profile
end
end
...
end
I saw this previous question and gave the answer a try but got an error NameError: uninitialized constant phone. Here is the code I tried from that:
Profile.all(:include => :items, :conditions => ["items.name = ?", phone])
Specifically I put that code in the following:
<%= pluralize(Profile.all(:include => :items, :conditions => ["items.name = ?", phone])).count, "person") %>
How can I do this?
The above was failing because I didn't have phone in quotations. Simple. The following worked:
Profile.all(:include => :items, :conditions => ["items.name = ?", "phone"])
And pluralizing:
<%= pluralize(Profile.all(:include => :items, :conditions => ["items.name = ?", "phone"]).count, "person") %>

simple_form custom input for a composed_of aggregate attribute in Rails

I'm trying to write a custom input for the simple_form gem for a Rails 3 model which has a composed_of aggregate attribute. See the example below.
I tried using fields_for but it generates params like "person"=>{"name"=>{"fname"=>"James","middle"=>"T","lname"=>"Kirk"} which does not get handled by Person.create nor update_attributes like an association would.
Gives the following error
undefined method `fname' for {"fname"=>"James", "middle"=>"T", "lname"=>"Kirk"}:ActiveSupport::HashWithIndifferentAccess
How would you implement this?
Example
$ rails g scaffold person last_name:string first_name:string middle_name:string
lib/fullname.rb
class Fullname
attr_reader :fname, :middle, :lname
def initialize(fname, middle, lname)
#fname, #middle, #lname = fname, middle, lname
end
end
app/models/person.rb
class Person < ActiveRecord::Base
composed_of :name,
:class_name => 'Fullname',
:mapping =>
[ # database # Fullname
[:first_name, :fname],
[:middle_name, :middle],
[:last_name, :lname]
],
:allow_nil => true
end
app/views/people/_form.html.haml
= simple_form_for #person do |f|
= f.input :name, :as => :fullname
= f.submit 'Save'
app/inputs/fullname_input.rb
class FullnameInput < SimpleForm::Inputs::Base
def input
#builder.simple_fields_for attribute_name, :validate => false do |form|
[ 'First:', form.input_field(:fname, :size => 10),
'Middle:', form.input_field(:middle, :size => 5),
'Last:', form.input_field(:lname, :size => 10)
].join(' ').html_safe
end
end
end