Hanami framework doesn't work with many to many through? - ruby

I used rich many to many...
class OwnerRepository < Hanami::Repository
associations do
has_one :avator
has_many :oneships#It's for join table..
has_many :avators,through: :oneships
end
def find_with_avator(id)
aggregate(:avator).where(id: id).map_to(Owner).one
end
def find_with_avators(id)
aggregate(:avators).where(id: id).map_to(Owner).one
#This found one avator but should be found two???
end
end
I create two owners and two avator for each owner,And every owner has two avators with through oneship.
But when i want to find avators with given owner_id,Give me one result.
In other side Avator Repository works fine..
Why?

Related

how to update/create has_one associated data efficiently in rails

Hi i am learning rails and i got the situation where i have to update or create the data for has_one associated model/table.
I tried this
Worker.rb
class Worker < ApplicationRecord
has_one :worker_encrypted_info
end
worker_encrypted_info.rb
class WorkerEncryptedInfo < ApplicationRecord
belongs_to :worker
end
workers_controller.rb
def update_personal_info
if #worker.update(update_personal_info_params)
#worker.create_worker_encrypted_info(sin: params[:worker_encrypted_info][:sin])
end
end
now what
#worker.create_worker_encrypted_info(sin: params[:worker_encrypted_info][:sin])
is doing is first updating worker_encrypted_info record with worker_id: nil and then creating new record for same worker_id instead of updating the old one.
which is not very great ofcourse because i dont need that worker_id: nil record in my database at all.
why do i need to delete that previous one manually with .destroy code?
is there any Efficient way to do so?
I would expect that this works
def update_personal_info
return unless #worker.update(update_personal_info_params)
if #worker.worker_encrypted_info
#worker.worker_encrypted_info.update(sin: params[:worker_encrypted_info][:sin])
else
#worker.create_worker_encrypted_info(sin: params[:worker_encrypted_info][:sin])
end
end

Setting up a many-to-many relationship in Active Record

I have two models: meal_plans and dinners. Each meal_plan will have a week's worth of dinners, and each dinner will be on, potentially, several meal_plans.
I'm wondering what the best approach is to save 7 dinner IDs into a meal plan. Initially, I was just going to save an array of dinner IDs into a meal plan's params with a helper:
def create_week_of_meals(week)
ids = []
week.each {|dinner| ids.push(dinner.id)}
return ids
end
With strong params:
params.require(:meal_plan).permit(:user_id, :meals)
This works alright, but it leaves me with a string of meal_ids which I would have to turn back into an array and then query Dinners for each one of those dinner ids. Is there a better way of doing this? I've seen a lot of references to rails accepts_nested_attributes_for but from what I can tell that mostly deals with saving some attributes from another model into the current record, whereas I'm looking to save a reference to several models.
I am familiar with has_many_through relationships but it seems like a lot of overhead to create a separate model and seven new records for each meal plan just to attach some dinner_ids to a record.
class Dinner < ActiveRecord::Base
has_many :meal_plan_placements
has_many :meal_plans, through: :meal_plan_placements
end
class MealPlan < ActiveRecord::Base
has_many :meal_plan_placements
has_many :dinners, through: :meal_plan_placements
end
class MealPlanPlacement < ActiveRecord::Base
belongs_to :dinner
belongs_to :meal_plan
end
This should work, but I haven't actually run it locally, so you should play around with it. You can also read more about the through option.

Activerecord/Datamapper - Have one child belong to many parents

How would you set up an activerecord/datamapper association for the following scenario:
A user creates a "bookshelf" which has many books(a book object just has an isbn that is used to query an api, and has_many review objects associated with it). Let's say Jack creates a "bookshelf" with a book object. Then, lets say that Jill creates a "bookshelf" with the same book object(it has the same id and the same reviews). The book object has the following code as of now:
class Book < ActiveRecord::Base
has_many :reviews
end
Then, when you view the page for a book (you click the link to it from the "bookshelf" created by Jack) you should see the same book object when you clicked the link to it from Jill's "bookshelf" (e.g. both "bookshelves" have a link to /books/23 because they have the same book object).
I have not been able to figure this out with the has_many association because that requires me to make a new book each time a user adds a book to their "bookshelf." I have trouble understanding the has_and_belongs_to_many relationship, is that what should be used here? I was not able to find any similar questions on SO, so any help is greatly appreciated.
I am using Rails 4 with Ruby 2.1.
Here is a drawing of what I would like to accomplish:
Drawing
Yes, you would have to define many-to-many relationship between a Bookshelf and a Book. There are two ways to achieve this in Rails:
Option 1) Use has_and_belongs_to_many
See guide
According to official documentation has_and_belongs_to_many association:
Specifies a many-to-many relationship with another class. This associates two classes via an intermediate join table. Unless the join table is explicitly specified as an option, it is guessed using the lexical order of the class names. So a join between Developer and Project will give the default join table name of “developers_projects” because “D” precedes “P” alphabetically.
So, your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_and_belongs_to_many :books
end
class Book < ActiveRecord::Base
has_and_belongs_to_many :bookshelves
has_many :reviews
end
Add a join table generation to your migrations:
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
end
end
end
This will create a books_bookshelves table in your database. The table will have no primary key. There would be two foreign keys to your models Book and Bookshelf.
So, if you call self.books in the context of an user's bookshelf, you will get a list of books in the bookshelf. Vice versa, calling self.bookshelves in the context of a book will return a set of bookshelves the book belongs to.
The problem with this approach is that every time you add a new book to the bookshelf a new record is created in the database. If you are okay with that, there is no easier option than using has_and_belongs_to_many association. Otherwise, I recommend you to go with the Option #2.
Option 2) Use has_many :through
Another option is to use has_many, :through association (see guide). You would have to define one more model to do that, but it might come handy in some use cases (see below for an example).
Your classes should look like this:
class Bookshelf < ActiveRecord::Base
has_many :books, through: :books_bookshelves
has_many :books_bookshelves
end
class Book < ActiveRecord::Base
has_many :bookshelves, through: :books_bookshelves
has_many :books_bookshelves
has_many :reviews
end
class BooksBookshelf < ActiveRecord::Base
belongs_to :book
belongs_to :bookshelf
end
Probably the best thing about using has_many :through association is that it allows you to add custom columns to the join table (e.g. add column count to keep track how many books of the same type are there in the bookshelf).
The migration would look pretty much the same as the one we used in Option 1, except for the fact we are adding an unique constraint on the foreign keys (please note that adding the constraint is optional):
class CreateBooksBookshelvesJoinTable < ActiveRecord::Migration
def change
create_table :books_bookshelves, id: false do |t|
t.belongs_to :book, index: true
t.belongs_to :bookshelf, index: true
# add your custom columns here
end
add_index :books_bookshelves, [:book_id, :bookshelf_id], unique: true # to make sure you won't create duplicate records
end
end
By going with this approach, adding a new would be a bit more complicated as you would have to make sure you are not inserting duplicate records in the join table. (However, you may remove the unique constraint from the migration, to achieve exactly the same kind of behavior as you would get with has_and_belongs_to_many.)

How to load parent-child-parent associations in Rails 4?

this is my first question on StackOverflow :)
I'm building a Rails 4 app, having trouble to figure out a good way to load records from mutilple data models. I could hard code SQL statements like an inner join, but wondering if there's any better way. Searched in existing questions on SO, but didn't find a match.
Here are my models:
class Person < ActiveRecord::Base
has_many :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
belongs_to :city
end
class City < ActiveRecord::Base
has_many :addresses
end
Question: given a person Id, how should I load its associated addresses with the city information?
Address.includes(:persons,:cities).where(person_id: person.id)
this is one of many ways.

Is there a way in MongoMapper to achieve similar behavior as AR's includes method?

Is there a feature equivalent in MongoMapper to this:
class Model < ActiveRecord::Base
belongs_to :x
scope :with_x, includes(:x)
end
When running Model.with_x, this avoids N queries to X.
Is there a similar feature in MongoMapper?
When it's a belongs_to relationship, you can turn on the identity map and run two queries, once for your main documents and then one for all the associated documents. That's the best you can do since Mongo doesn't support joins.
class Comment
include MongoMapper::Document
belongs_to :user
end
class User
include MongoMapper::Document
plugin MongoMapper::Plugins::IdentityMap
end
#comments = my_post.comments # query 1
users = User.find(#comments.map(&:user_id)) # query 2
#comments.each do |comment|
comment.user.name # user pulled from identity map, no query fired
end
(Mongoid has a syntax for eager loading, but it works basically the same way.)

Resources