#to_json_options - Define to_json options in the model classes # #=CONTACT DETAILS # # author: Heiko Seebach # email address: heiko (dot) seebach (at) web (dot) de # license: MIT # project website: http://to-json-options.rubyforge.org or http://rubyforge.org/projects/to-json-options/ # private website of the author: https://www.xing.com/profile/Heiko_Seebach # #=DESCRIPTION # #Since Rails 2.0, an ActiveRecord model is able to serialize itself as a JSON #string. Default is, that all attributes of the model are included in the #serialization result. # #This is done simply by calling: # # ModelClass.to_json # #For Details see #http://api.rubyonrails.com/classes/ActiveRecord/Serialization.html # #As described there, one can parameterize the to_json call, using keys #like :include, :only, :method or :except. #It's also possible to define the options for associate classes, as shown in #the above documentation example: # # konata.to_json(:include => { :posts => { # :include => { :comments => {:only => :body } }, # :only => :title } }) # #So the model classes have no clue about how they are serialized. #From one point of view, this is a clean approach, it's "separation of #concerns". # #But, what if you've a tree of associated model classes included in one to_json #call, or if you have different kinds of trees often including the same model #classes? Wouldn't it be a "railsy" approach, if you declared the serialization #parameters inside each model class? # #That's exactly, what this plugin is made for. # #=INSTALLATION # #Simply install using # script/plugin install http://to-json-options.rubyforge.org/svn/trunk/plugins/to_json_options #(or # ruby script\plugin install http://to-json-options.rubyforge.org/svn/trunk/plugins/to_json_options #under Windows) # #=USAGE # #==Basic # #First take a look at these examples: # # class User < ActiveRecord::Base # to_json_options :except=>:password # ... # end # #or # # class User < ActiveRecord::Base # to_json_options :only=>[:login, :id] # ... # end # # #As you can see, the options are simply declared inside the model instead in the #to_json call itself. This works for all the standard options (:only, except etc.) #as described in the above documentation. # #==Subclasses #If you override keys in subclassess, they are not merged like the standard #Ruby Hash merges, but they're added to the already existing values: # #This means, that # # class ExtendedUser < User # to_json_options :only=>:extended_login # ... # end # #will work as it was defined like this # # to_json_options :only=>[:login,:id,:extended_login] # # #You may also define several keys in one line like # # to_json_options :only=>[:login,:id], :methods=>:decrypted_password # # #==Context # #You can define options to be scoped in a context. Whenever you pass this context #in the parameters of the to_json_call, you can activate these options. If you omit a #context in the to_json call, all options that are defined only with a context will #be ignored. # #Example: # # class User < ActiveRecord::Base # to_json_options :very_verbose :except=>:password # to_json_options :not_very_verbose :only=>{:id, :login] # ... # end # #To activate the second declaration, you can simply call the to_json like this # # user.to_json :context=>:very_verbose # #Whenever you omit a name for the context, a context will automatically be created #and named :default #So calling # user.to_json :context=>:default #is similar to # user.to_json # #=to_xml #Simply replace all the above said about to_json with to_xml. It will behave exactly #the same. # #=RREQUIREMENTS # #Rails 2.0+ module ToJsonOptions #extend programmatically def self.included(base) base.extend(ClassMethods) end def self.merge_by_adding_values source, target source.each do |key,value| if target[key] target[key]=[target[key]] unless target[key].class == Array #ensure, it's an array value.class==Array ? target[key].concat(value) : target[key]<< value else target[key] = ( value.class == Array ? value : [value] ) end target[key].uniq! end end #how to do this better? mattr_accessor maybe? class GlobalState cattr_accessor :to_json_options end module ClassMethods def to_json_options context, options="only_1_param_called" include ToJsonOptions::InstanceMethods GlobalState.to_json_options||={} if options=="only_1_param_called" to_json_options :default, context #call with 2 params else GlobalState.to_json_options[self.to_s]||={} context = [context] unless context.class==Array for c in context GlobalState.to_json_options[self.to_s][c]||={} ToJsonOptions::merge_by_adding_values(options, GlobalState.to_json_options[self.to_s][c]) end end end def reset_to_json_options GlobalState.to_json_options||={} GlobalState.to_json_options[self.to_s]=nil end end module InstanceMethods def to_xml(orig_options={}) opts=modified_options orig_options super(opts ? opts : orig_options) end def to_json(orig_options={}) opts=modified_options orig_options super(opts ? opts : orig_options) end private def modified_options orig_options base_class_names=[] bc=self.class while bc!=nil base_class_names< to_json_options: #{self.class} -> #{opts.inspect}" opts end end end ActiveRecord::Base.send(:include, ToJsonOptions)