Form select for an array attribute - ruby-on-rails

I have a model Usergroup which has 2 attributes which are arrays:
Usergroup.create(name: "Group 1", account_id: 7, hix_modules: ['cs-seh','cs-ddr'], users: [61,83,77])
Now I want to create a form to create a usergroup. What is the best way to do this for the array attributes? I'm thinking of using selects combined with either Cocoon or Stimulus in the end to add a variable number of users or hix_modules. But to start simple with just one fixed select: how does it look like to send a valid array to the controller?

edit your model to make the field serialized to an array
class Usergroup < ActiveRecord::Base
serialize :hix_modules,Array
serialize :users,Array
end
test it out in console
a = Usergroup.new
=> #<Usergroup id: nil, hix_modules: [], users: [], name: nil, account_id: nil, created_at: nil, updated_at: nil>
a.hix_modules
=> []
a.hix_modules << "cs-seh"
=> ["cs-seh"]

I soled it by using a multi select:
= f.select :hix_modules, options_for_select(#hix_modules), { prompt: "Select module" }, { multiple: true, size: 10}
This sends these params values:
Parameters: {"usergroup"=>{"account_id"=>"7", "name"=>"Test", "hix_modules"=>["", "CS-Agenda", "CS-DDR"], "users"=>["", "65", "77", "46"]}

Related

Add nested attribute to associated model in Rails

I have three models using Active Record associations:
Book Model
has_many :checkouts
User Model
has_many :checkouts
Checkout Model
belongs_to :book
belongs_to :user
In my view, I need the book, checkout, and user names from the checkouts.
By using Book.first.checkouts I get:
#<ActiveRecord::AssociationRelation
[#<Checkout id: 30,
checkout_date: "2017-04-13",
return_date: nil,
book_id: 118,
user_id: 1,
created_at: "2017-04-13 17:43:07",
updated_at: "2017-04-13 17:43:07"
>,#<Checkout id: 50,
checkout_date: "2017-04-13",
return_date: nil,
book_id: 118,
user_id: 1,
created_at: "2017-04-14 00:33:34",
updated_at: "2017-04-14 00:33:34">
]>
But, I would like to the user name, not just the id. I've tried Book.first.checkouts.map { |c| c.user.name } but that returns only the name, and I need the rest of the checkout information. Ideally, my data (converted to json) looks something like:
{
name: "Book Name",
checkouts: [
checkout_data: "Today",
user_name: "Mary"
]
}
How can I add the user name to my checkout data?
You can try this at your controller:
render json: #books, include: { checkout: { only: :checkout_date, include: { user: { only: :name } } }}
You should preload the data to prevent (N+1) query problem,
For Display possible checkouts for particular book:
book_id = <given book id>
book = Book.find(book_id)
checkouts = book.checkouts.includes(:user)
return_hash = {name: book.name, checkouts: []}
checkouts.each do |checkout|
return_hash[:checkouts] << { checkout_data: checkout.checkout_date,
user_name: checkout.user.name
}
end
To include other solutions, I found this worked pretty well:
checkouts = book.checkouts.unreturned.map do |checkout|
checkout.attributes.merge({ user_name: checkout.user.name })
end
{ checkouts: checkouts, available: book.available?, book_id: book.id }
attributes.merge did the trick.

How to merge missing params fields from db to a params hash in rails

Lets say the app recieves a param hash like this
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sjfsj", "user"=>{"name"=>"Joe", "mobile"=>"12345678"}, "commit"=>"Save Changes"}
and this is the user model
{name: "value", mobile: "value", email: "value", many_others: "other_values" }
What am trying to do is create a new hash with all fields combined(params + missing_fields_from_db). So if the params has some missing keys it will be taken from model add to the new hash.
Like this:
{name: "Joe", mobile: "12345678", email: "value", many_others: "other_values" }
Is there a method available for that in ruby || rails?
Thank you
You are looking for a Hash#merge or Hash#reverse_merge. Difference:
a = { foo: 1, bar: 2, baf: 3 }
b = { foo: 2, bar: 1, baz: 1 }
a.merge(b)
#=> {:foo=>2, :bar=>1, :baf=>3, :baz=>1}
a.reverse_merge(b)
#=> {:foo=>1, :bar=>2, :baz=>1, :baf=>3}
Note, that Hash#merge is a pure Ruby method, Hash#reverse_merge comes from Rails.

Array of hashes where hash is returned by a function in jbuilder

I have a PORO TutorProfileHandler that has a function json that returns a hash.
class TutorProfileHandler
def initialize(opts)
#profile = opts[:tutor_profile]
end
def json
tutor = #profile.tutor
return {
id: tutor.id,
first_name: tutor.first_name,
last_name: tutor.last_name.first + '.',
school: #profile.school,
avatar: #profile.avatar.url,
bio: #profile.bio,
academic_level: #profile.academic_level,
headline: #profile.headline,
major: #profile.major,
rate: #profile.rate,
rating: #profile.rating,
courses: JSON.parse(#profile.courses),
video_url: #profile.video_url
}
end
end
In my index_tutor_profiles.json.jbuilder, I would like to generate
{
tutor_profile: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_sum: 20
}
However when I do this
json.tutor_profiles (#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
json.tutor_sum #tutor_sum
It gives me an empty array for tutor_profiles.
However if I move everything from TutorProfileHandler.json to the jbuilder file, it works. How do I explicitly include the hash returned by TutorProfileHandler.json in the jbuilder array?
Note: This returns an array, but it creates a new key-value pair array:
json.tutor_profiles json.array(#tutor_profiles) do |profile|
TutorProfileHandler.new({tutor_profile: profile}).json
end
Result:
{
array: [{id: 1, ...}, {id: 2, ...}, ...],
tutor_profile: [],
tutor_sum: 20
}
There is a ugly approach:
json.tutor_profiles #tutor_profiles do |profile|
tmp_json = TutorProfileHandler.new({tutor_profile: profile}).json
json.(tmp_json, *(tmp_json.keys))
end
I think the best practise is directly nesting inside model. You can get more information from the its github page.

Grouping by attribute of associated objects

class Event < ActiveRecord::Base
has_many :meetings
end
class Meeting < ActiveRecord::Base
belongs_to :event
end
How to write mysql query to search all events group_by meeting DATE(start_at)?
Event.inludes(:meetings).group ...
As a result I want to get a Hash:
{"2014-01-24"=>[#<Event id: , title: "First", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">, #<Event id: 2, title: "Second", created_at: "2014-01-24 16:02:52", updated_at: "2014-01-24 16:02:52">], "2013-01-29"=>[#<Event id: 3, title: "Third", created_at: "2013-01-29 05:30:40", updated_at: "2014-01-29 05:30:40">], ...]}
P.S: I am using PostgreSQL
Now I get it by this way:
hash = {}
Meeting.where("extract(month from start_at) = ?", Date.today.month).pluck('DATE(start_at)').uniq.each do |date|
hash[date] = Event.includes(:meetings).where("DATE(meetings.start_at) = ?", date).references(:meetings)
end
But it produced so many queries to the database :(
Event.joins(:meetings).group('meetings.start_at') should do. But want you want is a group_by array method http://apidock.com/ruby/Enumerable/group_by so what you should do is
#events.group_by {|e| e.meeting.start_date}
In case of many to many you should be better off with
result = Hash.new
Meeting.include(:events).each {|m| result[m.start_at]||=[]; result[m.start_at] << m.events}
and with one liner you could
Meeting.includes(:events).inject(Hash.new) do |result, m|
result[m.start_at]||=[]
result[m.start_at] << w.events
result
end
This code should execute two database calls i think

How to get one field over collection in mongoid/rails?

I have a model
class MyClass
include Mongoid::Document
include Mongoid::Timestamps
field :a, type: String
field :b, type: String
field :c, type: String
end
So how to get all the a-s, b-s or c-s as a list/array from all objects in the MyClass collection? Does Rails/Ruby/Mongoid have any syntactic sugar for this?
I know, it is possible to do so:
all_a = []
MyClass.desc(:created_at).each do |my_object|
all_a.push(my_object.a)
end
But I thought about:
MyClass.get(:fields => :a)
MyClass.get(:fields => :a,:b)
Update:
I found something:
MyClass.create(a: "my_a_string")
MyClass.create(a: "my_another_a_string")
Now:
MyClass.only(:a)
should work, but instead I get:
=> #<Mongoid::Criteria
selector: {}
options: {:fields=>{"a"=>1}}
class: MyClass
embedded: false>
When MyClass.only(:a).to_a
=> [#<MyClass _id: 525f3b9e766465194b000000, created_at: nil, updated_at: nil, a: "my_a_string", b: nil, c: nil>, #<MyClass _id: 525f4111766465194b180000,
created_at: nil, updated_at: nil, a: "my_another_a_string", b: nil, c: nil>]
But I thought about:
["my_a_string", "my_another_a_string"]
or
[{a: "my_a_string"}, {a: "my_another_a_string"}]
MyClass.only(:a), will return every other field as nil, and the selected fields with their values...
You can use MyClass.pluck(:a) instead.
But you can pass only one field.
If you want more than one field, you can do this:
MyClass.only(:a,:b).map {|obj| [obj.a,obj.b]}
MyClass.distinct(:a) is also sometimes better than pluck since it'll only return distinct values, which is better for populating dropdowns and so on.

Resources