Ruby on Rails multidimensional array-like storage - ruby-on-rails

I'm trying to create a form that has:
TextInput--Skill
DropDown--Years Experience
Using jQuery, I have the input and drop down fields populate if a user has more skills to add. Is it possible to store many sets of skills and their respective years experience in a database entry?
I've been looking into has_many:
Thank you!

No need for extra query here (has_many), look into rails serialization:
class User < ActiveRecord::Base
serialize :preferences, Hash
end
user = User.create(:preferences => { "background" => "black", "display" => "large" })
User.find(user.id).preferences # => { "background" => "black", "display" => "large" }
You can do the same with Array

Related

Can't access data in ActiveHash

I'm using the Gem active_hash https://github.com/zilkey/active_hash to create models for simple data that I don't want to create DB tables for.
For example, I have this model setup for FieldTypes:
class FieldType < ActiveHash::Base
self.data = [
{:id => 1, :name => "text", :friendly_name => "Text"},
{:id => 2, :name => "textarea", :friendly_ => "Text Area"},
{:id => 3, :name => "image", :friendly_ => "Image"},
]
end
And I'm trying to list these field types for a select:
def field_types_for_select
#FieldType.all.order('name asc').collect { |t| [t.friendly_name, t.name] }
FieldType.pluck(:friendly_name, :name)
end
But I get an error that order, collect or pluck are not defined.
How do I access this data? This works fine on other models, just not ActiveHash ones. According to the docs the model should work the same as ActiveRecord but I don't seem to be able to access it the same. FieldType.all works, but other methods do not.
Pluck isn't defined on ActiveHash::Base. It is defined on ActiveRecord::Relation::Calculations, and it's purpose is to produce a SQL select for the columns you specify. You will not be able to get it to work with ActiveHash.
You can, however, define your own pluck on your FieldType model.
def self.pluck(*columns)
data.map { |row| row.values_at(*columns) }
end
Or query the data directly:
FiledType.data.map { |row| row.values_at(:friendly_name, :name) }

Adding more than one object to a serialized text column

I'm interested in adding two objects to a serialized meta-data column in my activity feed model (Rails 3.1) to cut down on db calls.
Example: I have an Activity model with a data:text column which is serialized. I know I can add a Book object to this model and get it back as so:
test = Activity.create(:data => Book.find(1))
test.book.author # => James Joyce
Can I add two objects to this column (e.g. a Book and a User)? I tried using hashes/arrays but couldn't get them to work properly. Thanks in advance.
Here's how you might use a hash:
test = Activity.create(:data => {:book => Book.find(1), :user => User.find(1)})
test.data[:book] # => #<Book id:1 ...>
test.data[:user] # => #<User id:1 ...>

How do I seed a belongs_to association?

I would like to seed my Products and assign them to a specific User and Store.
Product.rb
class Product < ActiveRecord::Base
belongs_to :user
belongs_to :store
def product_store=(id)
self.store_id = id
end
end
Note: Store belongs_to Business (:business_name)
Seed.rb
This is my basic setup:
user = User.create(:username => 'user', :email => 'user2#email.com')
store = Store.create(:business_name => 'store', :address => 'Japan')
I attempted these but they did not work:
# This gives random ID's ranging from 1 to 4425!?
user.products.create([{:name => "Apple", :product_store => Store.find_by_address('San Francisco, USA')}])
# This gives me undefined method 'walmart'.
user.store.products.create([ {:name => "Apple"} ])
Is there a way to set the ID's so I can associate my Products to a Store and User?
UPDATE -
I have tried the answers below and still came out unsuccessful. Does anyone know of another way to do this?
Although it sounds like you found a workaround, the solution may be of interested to others.
From your original seeds.rb
user = User.create(:username => 'user', :email => 'user2#email.com')
store = Store.create(:business_name => 'store', :address => 'Japan')
Create the store
Store.create({
user_id: user.id
store_id: store.id
}, without_protection: true)
In the original code snipped "user" and "store" variables are declared. The code assigns user_id / store_id (the model columns inferred by the belongs_to relationship in the Store model) to the id values that are present in the "user" and "store" variables.
"without_protection: true" turns off bulk assignment protection on the id fields. This is perfectly acceptable in a seeds file but should be used with extreme caution when dealing with user provided data.
Or alternatively create your stores.
Then extract the correct one
e.g.
store = Store.find_by_business_name('Test Store')
and then create it based on that
e.g.
store.products.create(:product_name => "Product Test", :price => '985.93')
This will then set the relationship id for you,
If I'm not mistaken, you're just trying to do this.
user = User.create(:username => 'usertwo', :email => 'user2#email.com')
walmart = Store.create(:business_name => 'Walmart', :address => 'San Francisco, USA')
user.products.create(:name => 'Apple', :store => walmart)
Anything else required here that I'm not seeing?
Try doing this
store_1 = Store.new(:business_name => 'Test Store',
:address => 'Test Address',
:phone_number => '555-555-555')
store_1.id = 1
store_1.save!
The trick is not to set the id within the hash as it is protected.
Scott
What I did was update the particular products to a certain user, see this question:
Can I update all of my products to a specific user when seeding?
You could just create a series of insert satements for this "seed migration", including the record Id for each user, store, product etc. You might have to update database sequences after this approach.
Another approach
Create the initial records in you Rails app, through the GUI / web.
Then use something like Yaml-db. So you can dump the data to a yaml file. You can now edit that file (if necessary) and use that same file to seed another instance of the db with "rake db:load"
Either way.... You know the Ids will not be shifting around on you when these objects are created in the new db instance.
I'm sure there are other ways to do this... Probably better ones, even.
Here is a link to a write-up I did a while back for using yaml_db to seed an oracle database
http://davidbharrison.com/database_seeding_oracle
Try this:
User.destroy_all
Product.destroy_all
user = User.create!([{:username => 'usertwo', :email =>'user2#email.com'},
{:username => 'userthree', :email => user3#email.com}])
user.each_with_index do |obj, index|
Product.create!([{ :product_name => 'product #{index}', :user_id => obj.id }])
end
The table would look like this:
Here's how I prefer to seed an association in rails 6
#teacher = Teacher.new(name: "John")
#student = #teacher.build_student(name: "Chris")
#teacher.save!
#student.save!

Rails: Serialize value as comma-separated and not as YAML

I'm looking for a way to store a serialized value of eg. IDs in a column. In before claims that this is not an optimal design: the column is used for IDs of associated records, but will only be used when displaying the record - so no queries are made with selection on the column and no joins will be made on this column either.
In Rails I can serialize the column by using:
class Activity
serialize :data
end
This encodes the column as YAML. For legacy sake and since I'm only storing one dimensional arrays containing only integers, I find it more suitable to store it as a comma-separated value.
I've successfully implemented basic accessors like this:
def data=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:data, ids)
end
def data
(read_attribute(:data) || "").split(",")
end
This works pretty fine. However I'd like to add array-like methods to this attribute:
activity = Activity.first
activity.data << 42
...
How would I do this?
You can do it with composed_of feature as explained in this post.
It should be something like:
composed_of :data, :class_name => 'Array', :mapping => %w(data to_csv),
:constructor => Proc.new {|column| column.to_csv},
:converter => Proc.new {|column| column.to_csv}
after_validation do |u|
u.data = u.data if u.data.dirty? # Force to serialize
end
Haven't tested it though.
You can use serialize with a custom coder in rails 3.1.
See my answer to this question. :-)

Storing an array in the database in ruby on rails

I have somewhat of a unique situation, if I had a form with a checkbox for every state (as in US states, so 50 states say), I don't really want to add 50 columns to my db, how can I store them in an array in a single column?
I feel like I've seen this done but I'm having a hard time putting my finger on the implementation.
ActiveRecord::Base.serialize. Straight from the rails docs:
class User < ActiveRecord::Base
serialize :preferences
end
user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }
You could set up a States table with many to many relationship between User and State also. This would make queries more efficient.

Resources