First of all I push in my database in a field a Ruby object converted to JSON.
This object is (for the example) something like this :
{ #<Project id: 16 ...> => {#<Task id: 122 ...> => {details: {:a => 180, :b => 220}, sub: {...}}}}
And so on... This is just to show you that I have a nested hash with some objects in it.
This hash is pushed into my database in JSON with the following instruction Model.create(field: my_hash.to_json, ...). The field of the DB receiving this JSON Hash is a string.
Now when I want to retreive all of this data I just do this :
JSON.parse(Model.find(id)[:field])
But this gives me the keys that were object in strings. #<Project id: 16...> becomes "#<Project:0x000...>" and it's a string.
With this It's now impossible for me to do things such as var.keys.first[:id] #which in my example should be 16 since the key has became a string and is no longer a Ruby object.
Is it possible to retrieve my data the way it was pushed ?
Related
Im currently trying to merge two hashes, I don't really have much else to go on but this is the result I need is showed in this example;
{key_1: 'i want to replace this', key_2: 'i want to keep this'}.merge({key_1: 'new text'})
=> {key_1: 'new text', key_2: 'i want to keep this'}
Currently what I've got looks like this;
#notification.attributes.merge({body: ()}).to_json Im attempting to merge an replace the first key with the body element. What I'm really missing is the argument to perform the key replacement. If anyone has any direction, advice or even answers it would be much appreciated, thanks.
In Rails #attributes returns a hash with string keys:
irb(main):001:0> note = Notification.new(title: 'All your base are belong to us', body: 'Loren Ipsum...')
irb(main):002:0> note.attributes
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Loren Ipsum...", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}
If you want to replace a key in the hash you either need to use a hash with string keys:
irb(main):003:0> note.attributes.merge("body" => "Moahahahahahaha")
=> {"id"=>nil, "title"=>"All your base are belong to us", "body"=>"Moahahahahahaha", "read_at"=>nil, "created_at"=>nil, "updated_at"=>nil}
Or you need to change the keys of the hash to symbols which can be done with Hash#symbolize_keys:
irb(main):004:0> note.attributes.symbolize_keys.merge(body: "Moahahahahahaha")
=> {:id=>nil, :title=>"All your base are belong to us", :body=>"Moahahahahahaha", :read_at=>nil, :created_at=>nil, :updated_at=>nil}
This is a pretty common source of errors for new developers as Rails abstracts away the difference between symbol and string keys in many places through the use of ActiveSupport::HashWithIndifferentAccess or hash like objects like ActionController::Parameters that have indifferent access while Ruby itself is strict about the difference.
irb(main):008:0> { "foo" => "bar" }.merge(foo: 'baz')
=> {"foo"=>"bar", :foo=>"baz"}
irb(main):009:0> { "foo" => "bar" }.with_indifferent_access.merge(foo: 'baz')
=> {"foo"=>"baz"}
If you ever need to do this with a nested hash you can use the recursive versions deep_symbolize_keys and deep_merge.
You have to add ! in your merge operation, as when you are doing operation it is not effecting your actual object but will create new object. So your example as you said I did following to change values
{key_1: 'i want to replace this', key_2: 'i want to keep this'}.merge!({key_1: 'new text'})
# result
{:key_1=>"new text", :key_2=>"i want to keep this"}
please change following from
#notification.attributes.merge({body: ()}).to_json
to
#notification.attributes.merge!({body: ()}).to_json
Please try to change the order of master hash (the master is what you want to keep)
[9] pry(main)> a = {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
=> {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
[10] pry(main)> b = {:key_1=>"new text"}
=> {:key_1=>"new text"}
[11] pry(main)> c = b.merge(a)
=> {:key_1=>"i want to replace this", :key_2=>"i want to keep this"}
[12] pry(main)> d = a.merge(b); // <===== This is what you want.
=> {:key_1=>"new text", :key_2=>"i want to keep this"}
Hope it help. Thanks
I have defined user_params in rails 5, like this:
def training_plan_params
params.require(:training_plan).permit(
:training_plan_id,
:sport_id,
:distance,
lastTrainingStatus: [:duration,:reps,:type,:level],
trainings: [:text, :order, :level],
modified: [:level,:duration]
)
end
but when i send the params to rails, like this:
{ training_plan: { sport_id: 2, distance: 900 } }
it doesn't retrieve the distance in the controller as integer, like this:
=> <ActionController::Parameters {"sport_id"=>"2", "distance"=>"900"} permitted: true>
=> training_plan_params[:distance]
=> "900"
is there a way to keep its type and not converts it to string?
Was dealing with the same issue today, and I did some searching around.
I found this post: Rails test is converting my array of ints to an array of strings
Basically, the issue is with the way you're (we're) using ActionDispatch::IntegrationTest, not with ActionController::Parameters.
I solved this by converting the input to my PUT/POST call with .to_json and providing headers: { 'Content-Type': 'application/json' } to the PUT/POST call. That then led to the test router treating my data as json and preserving the typings, rather than defaulting to treating things as text.
My model has a custom_fields column that serializes an array of hashes. Each of these hashes has a value attribute, which can be a hash, array, string, or fixnum. What could I do to permit this value attribute regardless of its type?
My current permitted params line looks something like:
params.require(:model_name).permit([
:field_one,
:field_two,
custom_fields: [:value]
])
Is there any way I can modify this to accept when value is an unknown type?
What you want can probably be done, but will take some work. Your best bet is this post: http://blog.trackets.com/2013/08/17/strong-parameters-by-example.html
This is not my work, but I have used the technique they outline in an app I wrote. The part you are looking for is at the end:
params = ActionController::Parameters.new(user: { username: "john", data: { foo: "bar" } })
# let's assume we can't do this because the data hash can contain any kind of data
params.require(:user).permit(:username, data: [ :foo ])
# we need to use the power of ruby to do this "by hand"
params.require(:user).permit(:username).tap do |whitelisted|
whitelisted[:data] = params[:user][:data]
end
# Unpermitted parameters: data
# => { "username" => "john", "data" => {"foo"=>"bar"} }
That blog post helped me understand params and I still refer to it when I need to brush up on the details.
I am trying to get one record result from a collection of objects, but after following the Mongoid documentation I don't know what more to try.
I have this single element:
> contacts
=> #<Contact _id: 55ace6bc6xx, device_fields: {"app_id"=>"55ace6bc65195efc8200xxxx"}, created_at: 2015-07-20 12:17:00
UTC, updated_at: 2015-07-20 12:17:00 UTC, name_first: "Kory",
name_last: "Funk", ...>
this list of matchers:
> apps = []
> apps << App.where(id: "55ace6bc65195efc8200xxxx").first.id
=> ["55ace6bc65195efc8200xxxx"]
And this code trying to get the elements that match:
> contacts.elem_match(device_fields: {:app_id.in => apps }).to_a
=> []
> contacts.elem_match(device_fields: { "app_id": "55ace6bc65195efc8200xxxx"}).to_a
=> []
Why is it returning an empty array it there is one that matches?
According to official mongodb manual
The $elemMatch operator matches documents that contain an array field
And you are trying to use it with the hash field so you basically misunderstood this selection. So there is no object that matches.
Instead of it you should do:
contacts.where(:'device_fields.app_id'.in => apps).to_a
I can not achieve to resolve this with match_elem method, so finally I decided to make it through and. I am not very happy with this solution and I still don't understand why match_elem is not returning records, but at least I have found a solution to unblock this feature.
contacts.and(:device_fields.exists => true,
:device_fields.nin => ['', nil],
:"device_fields.app_id".in => apps).to_a
You don't need elemMatch here. It's for finding object array elements by partial matches (where you don't need full object equality, but only one or several fields)
This should work for your case.
contacts.where('device_fields.app_id' => {'$in' => apps})
I am consuming an API that expects me to do requests in the following format:
?filter=value1&filter=value2
However, I am using Active Resource and when I specify the :params hash, I can't make the same parameter to appear twice in the URL, which I believe is correct. So I can't do this:
:params => {:consumer_id => self.id, :filter => "value1", :filter => "value2" }, because the second filter index of the hash will be ignored.
I know I can pass an array (which I believe is the correct way of doing it) like this:
:params => {:consumer_id => self.id, :filter => ["value1","value2"] }
Which will produce a URL like:
?filter[]=value1&filter[]=value2
Which to me seems ok, but the API is not accepting it. So my question are:
What is the correct way of passing parameters with multiple values? Is it language specific? Who decides this?
http://guides.rubyonrails.org/action_controller_overview.html#hash-and-array-parameters
Try :filter[] => value, :filter[] => value2
to create a valid query string, you can use
params = {a: 1, b: [1,2]}.to_query
http://apidock.com/rails/Hash/to_query
http://apidock.com/rails/Hash/to_param
You can generate query strings containing repeated parameters by making a hash that allows duplicate keys.
Because this relies on using object IDs as the hash key, you need to use strings instead of symbols, at least for the repeated keys.
(reference: Ruby Hash with duplicate keys?)
params = { consumer_id: 1 } # replace with self.id
params.compare_by_identity
params["filter"] = "value1"
params["filter"] = "value2"
params.to_query #=> "consumer_id=1&filter=value1&filter=value2"