creating Models with sqlite3 + datamapper + ruby - ruby

How do you build a model with the following associations (i tried but couldn't get it to work):
each Order has: a Customer, a SalesRep, many OrderLine that each has an item.
I tried: when I do: Customer.all(Customer.orders.order_lines.item.sku.like => "%BLUE%")
the output is :[]
instead of: '[#<"Customer #id=1 #name="Dan Kubb">]'
When I delete SalesRep: it works.
Customer
has n, :orders
has n, :items, :through => :order
SalesRep
has n, :orders
has n, :items, :through => :order
Order
belongs_to :customer
belongs_to :technician
has n, :order_lines
has n, :items, :through => :order_line
OrderLine
belongs_to :order
belongs_to :item
Item
has n, :order_lines

Since the output isn't an error, it could be that you don't actually have the data in your database.
Try the following with irb -r your_models_file.rb:
- c = Customer.create(:name => "Dan Kubb")
o = Order.new(:customer => c) # Create and add technician unless it's :required => false
i = Item.create(:sku => "BLUE") # Plus any other required fields
ol = OrderLine.create(:order => o, :item => i)
o.order_lines << ol; o.save
That should create the records needed for this to work. Try this out, and if it doesn't work, post your entire models file so we can get a better idea of what's up.

Related

Many to Many relationship :through giving 'Could not find the association' error

In my model an Item is created by a User and can be purchased by many Users, and a User can purchase many Items.
User, Item, and Purchase are defined, using AcvtiveRecord with superfluous details snipped for brevity as follows:
class User < ActiveRecord::Base
# various other fields
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
class Purchase < ActiveRecord::Base
belongs_to :item
belongs_to :user
# various other fields
end
and an rspec test also snipped as follows:
describe "user purchasing" do
it "should allow a user to purchase an item" do
a_purchase = Purchase.create!(:item => #item, # set up in `before :each`
:user => #user # set up in `before :each`
)
a_purchase.should_not eq(nil) # passes
#item.buyers.should include #user # fails
#user.purchased_items.should include #item # fails
end
end
This results in
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #item.buyers.should include #user
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model Item
Likewise if I swap around #file_item.buyers.should include #user and #user.purchased_items.should include #item I get the equivalent
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #user.purchased_items.should include #item
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model User
My migration looks like
create_table :users do |t|
# various fields
end
create_table :items do |t|
t.integer :creator_id # file belongs_to creator, user has_many items
# various fields
end
create_table :purchases do |t|
t.integer :user_id
t.integer :item_id
# various fields
end
What have I done wrong?
You have to specify the following.
class User < ActiveRecord::Base
has_many :purchases
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
has_many :purchases
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
Only when you specify
has_many :purchases
the model will be able to identify the association.

ActiveRecord::HasManyThroughAssociationPolymorphicSourceError

I need a player to have many structures and the structure to belong to the player. Structure is a polymorphic relationship.
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structures, :through => player_structures
end
class PlayerStructures < ActiveRecord::Base
belongs_to :structure, polymorphic: true
belongs_to :player
end
class StructureA < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
class StructureB < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
But if I pull out Player.first and ask for its structures, it gives:
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'Player#structures' on the polymorphic object 'Structure#structure'.
But it should be able to generate a SQL query where it finds all player_structures with its id, then fetches the structure based on the structure_id and structure_type. Why does this fail and how can I validly construct a polymorphic join table?
UPDATE
If I do what I want it to do manually, it works:
player_structures.collect(&:structure)
Rails, y u no do that?
I think you need to be more specific in defining your relationships in your Player model. For example:
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structureas, :through => player_structures, :source => :structure, :source_type => 'StructureA'
has_many :structurebs, :through => player_structures, :source => :structure, :source_type => 'StructureB'
end
Then you can make a method that'll return all the structures defined in the relationships instead of having to access each one individually.

How to have an ordered model association allowing duplicates

I have two models, Song and Show. A Show is an ordered list of Songs, in which the same Song can be listed multiple times.
That is, there should be an ordered array (or hash or anything) somewhere in Show that can contain Song1, Song2, Song1, Song3 and allow re-ordering, inserting, or deleting from that array.
I cannot figure out how to model this with ActiveRecord associations. I'm guessing I need some sort of special join table with a column for the index, but apart from starting to code my SQL directly, is there a way to do this with Rails associations?
Some code as I have it now (but doesn't work properly):
class Song < ActiveRecord::Base
attr_accessible :title
has_and_belongs_to_many :shows
end
class Show < ActiveRecord::Base
attr_accessible :date
has_and_belongs_to_many :songs
end
song1 = Song.create(title: 'Foo')
song2 = Song.create(title: 'Bar')
show1 = Show.create(date: 'Tomorrow')
show1.songs << song1 << song2 << song1
puts "show1 size = #{show1.songs.size}" # 3
show1.delete_at(0) # Should delete the first instance of song1, but leave the second instance
puts "show1 size = #{show1.songs.size}" # 2
show1.reload
puts "show1 size = #{show1.songs.size}" # 3 again, annoyingly
Inserting might look like:
show1.songs # Foo, Bar, Foo
song3 = Song.create(title: 'Baz')
show1.insert(1, song3)
show1.songs # Foo, Baz, Bar, Foo
And reordering might (with a little magic) look something like:
show1.songs # Foo, Bar, Foo
show1.move_song_from(0, to: 1)
show1.songs # Bar, Foo, Foo
You're on the right track with the join table idea:
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
belongs_to :shows #foreign_key show_id
belongs_to :songs #foreign_key song_id
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items
has_many :songs, :through => :playlist_items
end
Then you can do stuff like user.playlist_items.create :song => Song.last
My current solution to this is a combination of has_many :through and acts_as_list. It was not the easiest thing to find information on combining the two correctly. One of the hurdles, for example, was that acts_as_list uses an index starting at 1, while the array-like methods created by the ActiveRecord association start at 0.
Here's how my code ended up. Note that I had to specify explicit methods to modify the join table (for most of them anyway); I'm not sure if there's a cleaner way to make those work.
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items, :order => :position
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
attr_accessible :position, :show_id, :song_id
belongs_to :shows
belongs_to :songs
acts_as_list :scope => :show
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items, :order => :position
has_many :songs, :through => :playlist_items, :order => :position
def song_at(index)
self.songs.find_by_id(self.playlist_items[index].song_id)
end
def move_song(index, options={})
raise "A :to option is required." unless options.has_key? :to
self.playlist_items[index].insert_at(options[:to] + 1) # Compensate for acts_as_list starting at 1
end
def add_song(location)
self.songs << location
end
def remove_song_at(index)
self.playlist_items.delete(self.playlist_items[index])
end
end
I added a 'position' column to my 'playlist_items' table, as per the instructions that came with acts_as_list. It's worth noting that I had to dig into the API for acts_as_list to find the insert_at method.

rails: how do I build an active-relation scope to traverse many tables?

I have these tables and relationships:
user has_many projects
project has_many tasks
task has_many actions
I would like to build a scope that allows me to select all of the current users actions, regardless of what project or task they belong to.
Thanks
I don't think scopes are necessary for this if you use the nested_has_many_through plugin.
class User < ActiveRecord::Base
has_many :projects
has_many :tasks, :through => :projects
has_many :actions, :through => :tasks
end
class Project < ActiveRecord::Base
has_many :tasks
has_many :actions, :through => :tasks
end
User.first.actions
I found something that works.
In the Actions model:
def self.owned_by (user)
joins("join tasks on actions.task_id = tasks.id").
joins("join projects on tasks.list_id = projects.id").
where("projects.user_id = ?" , user.id)
end
From the console:
u=User.find(1)
Action.owned_by(u).count
=> 521 # which is correct
I'm mot sure if its the best way to do it as I'm a bit new to sql. I get the feeling it could be made more concise.
EDIT Slightly better
Action.joins(:task => [{:project => :user }]).where(:projects => {:user_id => user.id })

Datamapper has n relationship with multiple keys

I am working on a simple relationship with DataMapper, a ruby webapp to track games. A game belongs_to 4 players, and each player can have many games.
When I call player.games.size, I seem to be getting back a result of 0, for players that I know have games associated with them. I am currently able to pull the player associations off of game, but can't figure out why player.games is empty.
Do I need to define a parent_key on the has n association, or is there something else I'm missing?
class Game
belongs_to :t1_p1, :class_name => 'Player', :child_key => [:player1_id]
belongs_to :t1_p2, :class_name => 'Player', :child_key => [:player2_id]
belongs_to :t2_p1, :class_name => 'Player', :child_key => [:player3_id]
belongs_to :t2_p2, :class_name => 'Player', :child_key => [:player4_id]
...
end
class Player
has n, :games
...
end
Still haven't figured out a way that feels right, but for now I am using the following workaround. Anyone know of a better way to accomplish this?
class Player
has n, :games # accessor doesn't really function...
def games_played
Game.all(:conditions => ["player1_id=? or player2_id=? or player3_id=? or player4_id=?", id, id, id, id])
end
end
Have you tried the following:
class Game
has n, :Players, :through => Resource
end
class Player
has n, :Games, :through => Resource
end
I'm looking for a related bug now.
You should be able to use Single Table Inheritance to achieve the desired result. Although you may need to do some thinking about how you to handle players being Player One in one game and Player Two in another.
My example code is just for reference. It has not been tested but should work.
class Player
property :id, Serial
property :name, String
property :player_number, Discriminator
end
class PlayerOne < Player
has n, :games, :child_key => [ :player1_id ]
end
class PlayerTwo < Player
has n, :games, :child_key => [ :player2_id ]
end
class PlayerThree < Player
has n, :games, :child_key => [ :player3_id ]
end
class PlayerFour < Player
has n, :games, :child_key => [ :player4_id ]
end
class Game
belongs_to :player1, :class_name => 'PlayerOne', :child_key => [:player1_id]
belongs_to :player2, :class_name => 'PlayerTwo', :child_key => [:player2_id]
belongs_to :player1, :class_name => 'PlayerThree', :child_key => [:player3_id]
belongs_to :player2, :class_name => 'PlayerFour', :child_key => [:player4_id]
end

Resources