Override to_xml for all objects in Rails 3 - ruby-on-rails

Looking to override to_xml method for everything in my app and having a tough time.
The reason is pretty trivial, I need to get rid of the :indent formatting it does by default. I've heard the arguments that "this is a view" problem, and that I should just override to_xml in my models where I need this.
The problem is that I return this stuff programmatically and having :skip_types set makes my responses incorrect. They're incorrect when you have an empty array, and you end up with:
\n
Suddenly it's a string value read by the client cotaining \n (<< there's a bunch of whitespace there, too, but stack overflow doesn't seem to like it). I also don't like the idea of having to override the same thing in 30 places.
That said, the fix is really easy, I just can't seem to put it in the right place. Just looking for help on where to put this. And a second set of eyes on my fix would be appreciated, too.
My fix is
alias __old_to_xml to_xml
def to_xml(options = {})
options.merge!(:indent => 0)
__old_to_xml(options)
end

I generally agree with the apprehension others have about the approach, but I think you could monkey patch it by adding a file to config/initializers with the following:
module ActiveRecord::Serialization
alias __old_to_xml to_xml
def to_xml(opts={})
__old_to_xml opts.merge(:indent => 0)
end
end
If you're on Rails 3, I believe you'll want ActiveModel::Serializers::Xml instead of ActiveRecord::Serialization, but no guarantees.

Related

A spelling, expected to be equivalent, causes error. Why?

I'm reading this question, where it says, that the calls
something {|i| i.foo }
something(&:foo)
are equivalent.
Now I was trying to refactor my model named AdminUser according to this pattern and replaced
after_create { |admin| admin.send_reset_password_instructions }
with
after_create(&:send_reset_password_instructions)
, but when I'm running my migration, which contains the lines
def migrate(direction)
super
# Create a default user
AdminUser.create!(email: 'a#b.de', password: 'very_clever', password_confirmation: 'very_clever') if direction == :up
end
it gives me the error
ArgumentError: no receiver given
pointing to the line AdminUser.create!....
Can anyone tell me what goes wrong here?
I know this is an older question, but it interested me quite a bit causing me to do some research of my own. I don't have a corrected code answer for you, but in looking around I believe this post what happened when pass a method to iterator method is very closely related and will most likely answer your question of "what's going wrong here?"
Basically because you're now passing it a proc with the refactored code it's expecting a specific argument that your AdminUser is no longer passing it and causing the error of it not having a receiver.
That being said, I'm sure you have your reasons of setting up your code the way you do, but based on the implied idea of what you're doing and the context I would agree with #photoionized with using
after_create :send_reset_password_instructions as it's clear, concise and (most likely) has your desired outcome.

Change "limit" clause in Geokit's "closest" scope

it sounds like not difficult at all but after looking for a while I can't find a way to change the limit of the closest scope in Geokit-rails.
I've tried to overide the definition of this method which is defined according to the documentation as:
def closest(options = {})
geo_scope(options).order("#{distance_column_name} asc").limit(1)
end
but with no success.
Any idea? Thank you very much!
That's an older version of geokit-rails/geokit, now the definition is:
def closest(options = {})
by_distance(options).first(1)
end
So no need to override anything, your code could simply say:
MyClass.by_distance(options).limit(my_limit)
Note: Try to avoiding monkey patching existing code. If you did what was suggested in another comment it might work now, but as soon as you update geokit/geokit-rails you risk it breaking as implementations do change (e.g. the closest method is implemented rather differently.
You can override this method, something like:
module Geokit
def closest(options = {})
geo_scope(options).order("#{distance_column_name} asc").limit(options[:limit] || 1)
end
end
and put this file in config/initializers folder and restart server. Note: put correct module name, classes. Consider it like a path to method.

Read json serialised objects back from a file

I am aiming to serialise a set of objects into a file so as to create a backup. I have the start of that working, using a methods on the models (simplified here, assuming I have two ActiveRecords foo and bar):
def backup(file, foo, bar)
file.write(foo.to_json(root: true))
file.write(bar.to_json(root: true))
end
This gives me a file as I desire, in this case with two records:
{"foo":{"Account_id":1,"Name":"F","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
{"bar":{"Account_id":1,"Name":"B","created_at":"2013-04-16T10:06:19Z","id":1,"updated_at":"2013-04-20T11:36:23Z"}}
At a later date I then want to read that backup in and reinstantiate those objects, probably then persisting them back to the database. My aim is to iterate through the file checking the type of each object, then instantiating the right object.
I have part of the logic, but not yet all of it, I haven't worked out how I determine the type of each serialised object before I instantiate it. The code I have for a restore is as follows:
def restore(file)
file.each_line do |line|
**<some magic that parses my line into objectType and objectHash>**
case objectType
when :foo
Foo.new.from_json(objectHash)
Foo.process
Foo.save!
when :bar
Bar.new.from_json(objectHash)
Bar.process
Bar.save!
end
end
end
What I'm looking for is the bit that goes in the "some magic" section. I can just write the code to parse the line directly to determine whether it's a foo or a bar, but I feel like there's probably some tricky Rails/Ruby way to do this that is automatic. Unfortunately, in this case Google is not being my friend. All I can see are pages that are focused on json in the web requests, but not parsing json back in this way. Is there something I'm missing, or should I just write the code to split the string directly and read the object type?
If I do write the code to split the string directly, I would write something along the lines of:
objectType = line[/^{"(\w*)"=>(.*)}/, 1]
objectHash = line[/{"(\w*)"=>(.*)}/, 2]
This is pretty ugly and I'm sure there's a better way (which I'm still looking into), but I'm not sure that this is even the right approach v's there being something that automatically looks at a json representation and knows from the root value what object to instantiate.
Lastly, the actual instantiation using from_json isn't working either, it isn't populating any of the fields on my ActiveRecord. It gives me nil parameters, so I think the parse syntax isn't right.
So, that makes three questions:
Is there a way to determine which object it is that I'm just missing, that is much cleaner?
If there isn't and I need to use a regexp, is there a syntax to get both bits of the line parsed in a single go, rather than my two lines with the same regexp?
The from_json syntax appears unhappy. Is there a syntax I'm missing here? (no longer a question - the code above is fixed, I was using as_json when it should have been to_json, although the documentation is rather unclear on that....)
(Note: edits over time to clarify my question, and because I've now got a regexp that works (didn't before), but still not sure it's very elegant.)
Further information - one of the problems here, as I dig into it further, is that the as_json isn't actually giving me json - what I have in the file is a hash, not json at all. Further, the values for created_at and lastupdated_at in the hash aren't quoted - so basically that's what's causing the parse on the way back in to fail. I've worked out that I should use to_json instead of as_json, although the documentation suggests that as_json should work.
I'm not sure I fully understand you're methodology, but I think using JSON.parse() would help.
There's some good information here http://mike.bailey.net.au/2011/02/json-with-ruby-and-rails/
This would help you translate the raw object back to a hash.
OK, so I think I've got something that works. I'm not convinced at all that it's elegant, but it gives me the result. I'll spend some time later trying to make it cleaner.
The code looks like this:
file.each_line do |line|
objectType = line[/^{"(\w*)":(.*)}/, 1]
objectJSON = line[/{"(\w*)":(.*)}/, 2]
objectHash = JSON.parse(objectJSON)
case objectType
when 'foo'
restoredFoo = Foo.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredFoo.created_at = objectHash['created_at']
restoredFoo.updated_at = objectHash['updated_at']
restoredFoo.save!
end
when 'bar'
restoredBar = Bar.new(objectHash.except('id', 'created_at', 'updated_at'))
restoredBar.created_at = objectHash['created_at']
restoredBar.updated_at = objectHash['updated_at']
restoredBar.save!
end
end
Items of note:
I feel like there should be a way to create the object that isn't a JSON.parse, but rather would make use of the from_json method on the model. I'm not sure what the from_json is good for if it doesn't do this!!
I'm having fun with mass_assignment. I don't really want to use :without_protection => true, although this would be an option. My concern is that I do want the created_at and updated_at to be restored as they were, but I want a new id. I'm going to be doing this for a number of entities in my application, I didn't really want to end up replicating the attributes_protected in the code - it seems not very DRY
I'm still pretty sure my reg exp can give me both objectType and objectJSON in one call
But having said all that, it works, which is a good step forwards.

Is there an idiomatic ruby/rails way of returning the first truthy mapped value?

I have an array of objects, some of which respond to :description, and I want to get the description from the first one with a truthy description. I could do this:
objects.detect{|o| o.try(:description)}.description
or this:
objects.map{|o| o.try(:description)}.detect{|o| o}
but the first isn't DRY (description is in there twice) and the second iterates through the whole array before finding the value. Is there anything in the ruby standard library, or in Rails' extensions to it, which would let me do something like this:
objects.detect_and_return{|o| o.try(:description)}
I know I could write it easily enough, but the standard libraries are big enough that I might not need to. Is there a function which works like my detect_and_return?
I haven't seen such a method, and the closest I found was a method capture_first which I found in the gem merb-cache. Seems they stumbled on the same problem and implemented this:
module Enumerable
def capture_first
each do |o|
return yield(o) || next
end
nil
end
end
You could also take a look at the Array and Enumerable methods in the Ruby facets library and see if you find something similar. Facets contains quite a lot of goodies, so you might get lucky.

Is there any way to define a model's attribute as always html_safe?

I have a model called Feature with a variable called body_string, which contains HTML markup I'd like to render, rather than escape.
Every time I reference body_string in my views, I need to use <%=raw or .html_safe. This seems redundant and not-so-DRY.
Is there any way that I can establish once-and-for-all the body_string variable as html_safe?
I'm assuming this would happen in the app/models/feature.rb file, but I can't figure out what the right syntax would be, exactly. I've thought of this:
def body_string
return self.body_string.html_safe
end
But Rails doesn't like it; it raises a stack level too deep exception.
Naturally I could define a variable/method with a different name:
def safe_body_string
return self.body_string.html_safe
end
And then just change all references in the views from body_string to safe_body_string. But somehow this seems almost as un-DRY as simply using raw or .html_safe in the first place.
Any insights to how best to handle this? I feel like there must be something really elegant that I'm just not seeing.
Just use read_attribute to avoid the recursive call to body_string:
def body_string
read_attribute(:body_string).html_safe
end
read_attribute is complemented by write_attribute for setting attributes from within your model.
A note on style: Don't use explicit returns unless you actually need them. The result of the last statement in a method is implicitly the value returned from the method.
While #meager's answer will definitely work, I don't think this logic belongs in a model. Simply because it adds view-level concerns (HTML safeness) to the model layer, which should just include business logic. Instead, I would recommend using a Presenter for this (see http://nithinbekal.com/posts/rails-presenters/ or find a gem for this -- I personally love Display Case). Your presenter can easily override the body_string method and provide the .html_safe designation when displaying in the view. This way you separate your concerns and can continue to get body_string from other models without mixing in the view concern.
Maybe this gem is useful for you. I also wanted to stop repeating html_safe all the time when the content is completely trustable.
http://rubygems.org/gems/html_safe_attribute
Or you can also use this approach,
def body_string
super && super.html_safe
end

Resources