Extract `Moped::BSON::Document` attributes in Ruby hash - ruby-on-rails

In Mongoid 3.0.21, how to get all model's attributes as a plain Ruby Hash?
Calling either #attributes or #raw_attributes returns Moped::BSON::Document. While it actually extends Hash, several hash method does not work as expected. Particularly #except returns unmodified self, not hash with given keys stripped off.
Update: Moped::BSON::Document properly inherits behavior of Hash. I was trying to name attributes with symbols, not strings, that's why #except didn't work. Shortly: do except('pictures'), not except(:pictures).

Hash[e.attributes]
where e is your model instance

I apologize for bumping something so old, but I wanted to leave this here for myself and all the future people who run into this same issue. I am using the Mongoid ORM for Rails, which uses Moped internally for its interaction with MongoDB.
This gem has now saved me hours and hours of manually converting things to Hash or HashWithIndifferentAccess: https://github.com/mindscratch/mongoid-indifferent-access.
Essentially it seems to have some sort of pre-return hook that automatically converts all documents coming from MongoDB to type HashWithIndifferentAccess.
Not looking for points on this. Just wanted to leave this here because it is the top Google result for this issue and it saved me from going insane.

Related

NoSQL Injection with a simple find_by in RoR

I am new to RoR and I have a basic question.
A colleague told me that the following code can cause a NoSQL Injection:
User.find_by(username: param[:username])
I know that I am not supposed to read directly from param but I still think that the code cannot generate any NoSQL injections. Am I wrong?
Your colleague is wrong, and should produce a working exploit if he believes this is true.
Providing a hash of key/value pairs to Rail's find_by or where methods is the intended, secure, and correct way of using those methods. The keys and values are always made safe before being used in a query.
There are certain problems that Rails will not protect you from, for example, the value of params[:username] could be a gigabyte of text, a File object, a nested hash of key/value pairs, etc. but this isn't an "injection" per se. Any special characters that could cause the meaning of the query to be changed will have no effect.

Select query adds attr_encrypted attribute in result even if not requested

I am struggling with an issue with a Rails 5.2.4.1 app.
Configuration is the following:
Ruby 2.6.5
Rails 5.2.4.1
attr_encrypted 3.1.0
I have a model called Chicken that has 2 attributes: name - which is attr_encrypted and number - which is a normal integer field. Whenever I perform queries to retrieve any other fields except the attr_encrypted one, that still gets attached to the result and it's alway nil:
Chicken.select(:number) => #<ActiveRecord::Relation [#<Chicken id: nil, number: nil, name: nil>]>
Please keep in mind that this is just a test application and the queries that I am trying to execute on the actual app where I have encountered this initially, are more complex.
Is there a way to prevent attr_encrypted from attaching encrypted fields to queries results? Since the current results mean that I have to re-write all the existing queries in the app or add a filter for these types of fields somehow
This problem was caused by this change to attr_encrypted. As far as I can tell, there isn't any easy way to remove this attribute without modifications to the library but no one actively works on it so that seems unlikely.
The only options as far as I can see are to:
Use another library
Override the models attributes method to exclude the value (may produce undesirable results). It will still show in other methods active record provides.
Deal with it
Something else I haven't been able to find
A few ways you can deal with it:
Use a library to generate JSON responses for the frontend to only include attributes you want
redefine serializable_hash like devise does to remove attributes. (A lot safer than redefining the attributes method itself.

Rails - ActiveModel::Serializer virtual attribute error

I'm using the active_model_serializers gem for a RoR API. Versions:
Rails: 4.2.8
Ruby: 2.2.5
active_model_serializers: 0.10.0
I'm using a virtual attribute in a serializer. I get it by using a sub query when I retrieve the objects from the database.
You can find the code here: Github Gist
This is the error I'm getting:
undefined method 'number_of_reservations' for DiscountSchedule...
This field isn't defined in the table nor in the model (attr_accessor)
I'm not sure why it doesn't work, I have a very similar serializer and it's working OK.
Any help will be appreciated.
EDIT:
I have another serializer where the virtual/calculated field is working OK. My guess on this is that since AR is making a bunch of LEFT OUTER JOINS and the SELECT list of the query is very big at some point something is getting broke.
The link won't work for me as I don't have access at my work place, however, from the error I can recommend you to check if you have defined the attributes like this in your serializer attributes :number_of_reservations and have an action in the serializer that says
def number_of_reservations
// Your logic goes here.
end
I suspect this question has to be about ActiveRecord, rather than AMS. You're trying to use select and alias to collect some computed (aggregate) attribute along with objects themselves. This, unfortunately, won't work in ActiveRecord, at least not in versions below 4.2.X. And this is why you're observing this behavior, there is no number_of_reservations in your models.
To see what's going on, try to inspect #objects here: https://gist.github.com/LuisDeHaro/ebf92781b449aa1ee7b85f8f552dd672#file-some_controller-rb-L17
Indeed: the issue was by the big amount of LEFT JOINS that the includes(:table_name) is generating. The serializer then does not know what to do.
I found a monkey-patch gem that works for AR (Rails 4 & 5) that fix this.
https://github.com/alekseyl/rails_select_on_includes
So, the virtual field number_of_reservations is picked up by the serializer like a charm.
And, you might be wondering: why do you want to retrieve a field that is not in the table definition in the database. A: well, in some scenarios you will need a calculated field for EVERY row you are retrieving. A SQL sub query is one of the most efficient ways to do so.
It's working now for me.

mongoid splits nested attributes in two hashes?

I've got a model List with nested attributes from another one, Article. What bugs me is I have Ruby request params having my first nested attribute in Article hash, and all the others(2nd,3rd article data, etc) in article_parameters.
I follow the standard mongoid tutorial here.
I have #list.articles.build in create method of List controller.
Can I influence what goes into build method? I've tried to call build(all_the_correct_hashes) but it does not seem to fix the issue.
So, in the nutshell I have 2 questions.
why articles gets only the first nested attribute, 2nd and next go to article_parameters hash?
why list.articles.build only creates _id in my Mongo document, but does not populate it with other fields?
I'll write up my solution, since I see people upvoting my question.
Frankly, I did not find any answer as to why such behaviour occurs.
The solution is a bit hackish.
In your save controller, call List.article.build(id => params[:id], someotherstuff => params[:someotherstuff])
Everything you pass to build in this case will populate article_parameters hash.
id and sometherstuff are fields of my Article model.
I feel this should be done implicitly by Mongoid, but it just does not work. Their development does not see anything wrong with it, either.

How to iterate ActiveRecord Attributes, including attr_accessor methods

I've looked everywhere for an elegant solution. The essential problem seems to be that ActiveRecord attributes that map to database columns are handled completely differently in ActiveRecord::Base than attr_accessor methods.
I would like to do something like:
model.attribute_names.each do |name|
# do stuff
end
in a way that also includes attr_accessor fields, but not any other instance methods. I know this in not built-in, but what is the most elegant way to do it?
You can't really solve this. You can approximate a hack, but it's not something that will ever work nicely.
model.attribute_names should get you all the ActiveRecord ones, but the attr_accessor fields are not fields. They are just ordinary ruby methods, and the only way to get them is with model.instance_methods.
Idea 1
You could do model.attribute_names + model.instance_methods, but then you'd have to filter out all your other normal ruby methods initialize, save, etc which would be impractical.
To help filter the instance_methods you could match them up against model.instance_variables (you'd have to account for the # sign in the instance variables manually), but the problem with this is that instance variables don't actually exist at all until they are first assigned.
Idea 2
In your environment.rb, before anything else ever gets loaded, define your own self.attr_accessor in ActiveRecord::Base. This could then wrap the underlying attr_accessor but also save the attribute names to a private list. Then you'd be able to pull out of this list later on. However I'd advise against this... monkey-patching core language facilities like attr_accessor is guaranteed to bring you a lot of pain.
Idea 3
Define your own custom_attr_accessor in ActiveRecord::Base, which does the same thing as Idea 2, and use it in your code where you want to be able to retrieve the attribute names. This would be safe as you won't be clobbering the built-in attr_accessor method any more, but you'll have to change all your code to use custom_attr_accessor where neccessary
I guess in summary, what are you trying to do that needs to know about all the attr_accessor fields? Try look at your problem from a different angle if you can.
I came here looking to do the same thing, and found out it was the wrong approach altogether thanks to Orion's answer.
Incase anyone else's use case is similar to mine, here's my solution. I was using attr_accessor to add extra properties to the models after querying them from ActiveRecord. I then wanted to output the results as JSON etc.
A better solution is to first convert the Models from ActiveRecord into regular hashes, and then add the attr_accessor properties as regular hash keys.
Example:
model_hash = model_from_activerecord.attributes.to_options
model_hash[:key] = value
The solution I came up with for myself builds upon Orion Edwards' answer.
The code:
klass_attributes = klass.column_names + klass.instance_methods(false).
map(&:to_s).keep_if{|a| a.include?('=')}.map{|a| a.sub('=', '')}
The breakdown:
klass.instance_methods(false) brings back only instance methods, and not inherited methods.
map(&:to_s) converts the array of symbols into an array of strings so we can use the include? method. Also needed to merge with array of strings returned by column_names.
keep_if{|a| a.include?('=')} will remove all strings within the array that do not have an equals sign. This was important, since I noticed that attr_accessor attributes all had '='. Ex: 'app_url' vs 'login_url='
map{|a| a.sub('=', '')} is the final piece that then removes the '=' from each string in the array.
You end up with an array of strings that represent attributes, including attr_accessor attributes.

Resources