Rails to_xml vs as_json - ruby-on-rails

Trying to mirror my API responses with as little code duplication as possible and have this so far....
Really, this is a "There has to be a better 'Rails way' to accomplish this..." question.
class Quote < ActiveRecord::Base
belongs_to :author
has_many :votes
def as_json(options={})
hash = super(except)
hash[:author] = self.author.name
hash[:vote_count] = self.votes.count
hash
end
def to_xml(options={})
hash = super(except)
hash[:author] = self.author.name // <---- line 14
hash[:vote_count] = self.votes.count
hash
end
private
def except
{ :except => [ :id, :created_at, :updated_at, :author_id ] }
end
end
JSON response works like a champ, but the xml throws this error
can't convert Symbol into Integer
app/models/quote.rb:14:in `[]='
app/models/quote.rb:14:in `to_xml'
As a secondary question, is the the best way to customize the output like I am? I'd like to not duplicate this logic if I can avoid it.
hash[:author] = self.author.name
hash[:vote_count] = self.votes.count
hash

to_xml returns an XML string, not a hash. That's why it's surprised by a symbol in the brackets: it thinks you're trying to modify a particular character, e.g. name[0] = 'A'
If you're interested in changing bits of the XML output, maybe you should just build a new hash of the attributes you want and run to_xml on that.

Related

The value of Store_accessor is not modified from the database?

In my model I have results
store_accessor :results
and in controller its like
if current_user.test
#results = current_user.test.results
end
And I am printing this value at the front end it keeps showing 0
def update
stats = user.test
stats.data_will_change!
attributes = { results: "#{(stats.results.to_i) + 1}" }
stats.update_attributes(attributes)
end
It's going through the function but the value at the front-end is still 0
store_accessor requires at least two attributes
store_accessor :column_name, :attribute
:column_name column is presumed to be Hash like type eg Hstore in PostgreSQL or json or something like that.
:attribute than behaves like standard database column in ActiveRecord model.
You can then call something like:
model.attribute = 5
model.save
It also works with mass assignment
model.update(attribute: 5)
In your case it could look something like this:
store_accessor :results, :stats
def update
stats = user.test
stats.update_attributes(stats: stats.results.to_i + 1)
end

How to store string as array in database column using Ruby on Rails

This question is asked many times on SO. The main problem is nothing got fits into my situation.
Case is, I am not able to store typed content as array in database column.
text_field whose code is:
= text_field_tag 'product[keywords][]', #product.keywords, class: 'tab-input
product_keywords'
In controller strong parameters are:
params.require(:product).permit(:id, :name, :keywords => [])
Jquery code that is not not removing value upon deletion when typed wrong value but it add commas after each element as I want to take commas seperated value in one column.
$(document).on 'keyup', '.product_keywords', ->
keyword = #value.replace(/(\w)[\s,]+(\w?)/g, '$1, $2')
if keyword != #value
#value = keyword
return
model code:
serialize :keywords, Array
migration code:
class AddKeywordsToProducts < ActiveRecord::Migration[5.1]
def change
add_column :products, :keywords, :text
end
end
So, if someone writes, abc and hit space a comma is added in the end. after three typed words it will look like:
abc, dbx, she
now I want to store it as array in column but its not storing properly.
it stores as:
["abc, dbx, she"]
Also please can anybody tell me the best cases to handle these cases?
Plus best practices to deal with such cases using ruby so I will learn it for future?
You probably want a custom serializer as shown here. So instead of:
serialize :keywords, Array
You might do somewhat like:
serialize :keywords, KeywordSerializer
And somewhere in helpers:
class KeywordSerializer
def self.dump(what)
what.join(", ")
end
def self.load(what)
what.split(/\s*,\s*/)
end
end
Passing array elements using single form tag is not possible to pass as a array and passing array as a string, you need to process it near white-listing your params,
permitted_params = params.require(:product).permit(:id, :name, :keywords => [])
permitted_params[:keywords] = permitted_params[:keywords][0].split(/\s*,\s*/)

Nested attribute_accessible rails or hash in attribute_accessible

I want to be able to create attributes on a ActiveRecord:Base Model that are nested.
For example -
class Book < ActiveRecord::Base
attr_accessor :operator, :who
I would like :who to have further attributes like :family, :me
So finally I can access these as follows
book = Book.new
book.who.family = [1,2,3]
book.who.me = 1
I also want to know how can I define the kind of values that attributes can take so I do not have to do that at runtime.
Currently am using something like this
after_initialize do
#who = {family: [], me: nil}
end
I tried it in my console. So if you add the your attributes as attribute accessors and after_initialize set them like this.
attr_accessor :who, :operator
after_initialize do
self.who = {family: [], me: nil}
self.operator = "Minus"
end
then you can access them like this
self.who[:family] = [1,2,3,4]
or
self.operator = "Minus"
and you can simply access them like this
self.who[:family] ==> [1,2,3]
Will this help?

How to parse param comma separated string ids to has_many mongoid field

How to handle tag_ids in post params to save it in related model? I would like to use for it only post_params method.
has_many :tags
def post_params
params.require(:post).permit(:title, :message, :tag_ids)
end
#Parameters: {"post"=>{"title"=>"asdf", "message"=>"asfd", "tag_ids"=>"543d727a4261729ecd000000,543d8a914261729ecd010000"}}
I've got:
Mongoid::Errors::InvalidValue -
Problem:
Value of type String cannot be written to a field of type Array
I found solution but I don't like it, in Post model I've added:
def tag_ids=str
str.split(',').each do |id|
t = Tag.find(id)
self.tags << t
end
end
I think that you have to modify the incoming data in tag_ids in create action in your controller.
So when you receive the data, before you are saving the data into DB by, for example: post.create! you should add parsing to your PostsController action create:
If you want array of string:
post.tag_ids = params[tag_ids]split(",")
or if you want array of integers:
post.tag_ids = params[tag_ids]split(",").map(&:to_i)
Something like this? (I'm assuming you want an array of ObjectId)
id_array = params['post']['tag_ids'].split(',').map { |id| BSON::ObjectId.from_string(id) }
params['post']['tag_ids'] = id_array

2 level deep grouping in ruby

So I have a array of records and I would like to group by 2 levels
Essentially I would like to group_by{|x| x.field1 } and then each value in hash to be further grouped in by field2. Effectively leading to a tree that I can dump out.
def treemaker(array = [])
tree = ledgers.group_by{|x|x.master_group}
tree.each{|x,z| tree[x] = z.group_by{|y| y.account_group}}
tree
end
I would then render tree in a way that i can be put into a "tree" javascript plugin.
Is there a more efficient way?
Sample Input: An Array of ActiveRecord objects, where the model contains, fields master_group, account_group and name
Class MyModel < ActiveRecord::Base
validates :master_group, :account_group, :name, :presence => true
end
Sample Ouput:
{"master_group1" => {"account_group1" => ["name1","name2",...],
"account_groupx" => ["name3", "name4",...],
....},
"master_group2" => {"account_group2" => ["namex", "namey"]},
...
}
I'm not specifically looking for an "SQL grouping" solution (but that would be nice too). Just a solution using enumerables on a any given list of ruby objects.
#xaxxon sent me thinking in the right way basically with the "default value of hash" path.
I think i can now add a method to my model where i can use all sorts of scopes and tack on tree at the end to get my models in tree mode.
class MyModel < ActiveRecord::Base
validates :master_group, :account_group, :name, :presence => true
def self.tree(field1 = 'master_group', field2 = 'account_group')
tree = Hash.new{|hash,key| hash[key] = Hash.new{|h,k| h[k] = []}}
all.each do |item|
tree[item.send('field1')][item.send('field2')].push(item)
end
tree # bob's your uncle!
end
end
MyModel.recent.tree => Hash of Hash of arrays
set up some fake data
foo=[{:a=>1,:b=>2},{:a=>3,:b=>4}]
set up the output data structure
tree={}
populate the output data structure - this is weird looking because you have to populate the hashes that don't exist when they don't exist, hence the ||={} stuff.
foo.each{|thing| (tree[thing[:a]]||={})[thing[:b]]=thing}
looks good.. :a is your master group and :b is your account_group
pp tree
{1=>{2=>{:a=>1, :b=>2}}, 3=>{4=>{:a=>3, :b=>4}}}

Resources