How to convert a Ruby object to JSON - ruby-on-rails

I would like to do something like this:
require 'json'
class Person
attr_accessor :fname, :lname
end
p = Person.new
p.fname = "Mike"
p.lname = "Smith"
p.to_json
Is it possible?

Yes, you can do it with to_json.
You may need to require 'json' if you're not running Rails.

To make your Ruby class JSON-friendly without touching Rails, you'd define two methods:
to_json, which returns a JSON object
as_json, which returns a hash representation of the object
When your object responds properly to both to_json and as_json, it can behave properly even when it is nested deep inside other standard classes like Array and/or Hash:
#!/usr/bin/env ruby
require 'json'
class Person
attr_accessor :fname, :lname
def as_json(options={})
{
fname: #fname,
lname: #lname
}
end
def to_json(*options)
as_json(*options).to_json(*options)
end
end
p = Person.new
p.fname = "Mike"
p.lname = "Smith"
# case 1
puts p.to_json # output: {"fname":"Mike","lname":"Smith"}
# case 2
puts [p].to_json # output: [{"fname":"Mike","lname":"Smith"}]
# case 3
h = {:some_key => p}
puts h.to_json # output: {"some_key":{"fname":"Mike","lname":"Smith"}}
puts JSON.pretty_generate(h) # output
# {
# "some_key": {
# "fname": "Mike",
# "lname": "Smith"
# }
# }
Also see "Using custom to_json method in nested objects".

Try it. If you're using Ruby on Rails (and the tags say you are), I think this exact code should work already, without requiring anything.
Rails supports JSON output from controllers, so it already pulls in all of the JSON serialization code that you will ever need. If you're planning to output this data through a controller, you might be able to save time by just writing
render :json => #person

Related

How to reuse symbols from a hash in another method in Ruby

I have the following method that defines a hash with a number of keys (there are a lot, I just cut it down for this example).
def data
#data ||= {
name: "Some Name",
email: "my#email.com"
}
end
Now, each of those keys I want to use in another method within the same class like so:
[:name, :email].each { |key| define_method("get_#{key}") { data[key] } }
While this works as it should, it doesn't seem to be a very good idea to hardcode the keys - I'd would much rather make them dynamic and have them reused from the hash I created within the first method. Since I am calling upon an Instance of this Class from another Class I get the following error when using the obvious approach:
data.keys.each { |key| define_method("get_#{key}") { data[key] } }
# => undefined local variable or method `data' for #<Class:0x0000000dc55938>
Any ideas how this could be solved?
As both methods are in same class why not use data instead of #data
2.1.2 :001 > def data
2.1.2 :002?> #data ||= {
2.1.2 :003 > name: "Some Name",
2.1.2 :004 > email: "my#email.com"
2.1.2 :005?> }
2.1.2 :006?> end
=> :data
2.1.2 :007 > data.keys.each { |key| define_method("get_#{key}") { data[key] } }
=> [:name, :email]
2.1.2 :008 > get_name
=> "Some Name"
2.1.2 :009 >
You can define method like data_keys. Use this method outside of your class and get keys.
class YourClass
def self.data
...
end
def self.data_keys
#data_keys ||= data.keys
end
end
YourClass.data_keys.each { |key| define_method("get_#{key}") { YourClass.data[key] } }
you can try something like this:--
def data(*args)
options = args.extract_options!
options ||= {
name: "Some Name",
email: "my#email.com"
}
##save it to instance variable for further use
#data=options
##either pass to another method
other_method(options)
##or call any other method to call private methods as well using send
new_obj =OtherObject.new
new_obj.send(:method_name,options)
end
same solution ,,using class variable
def data
##data ||= {
name: "Some Name",
email: "my#email.com"
}
end
in this way..you can even access data in model
def get_data
## ##data is available
end
If i'm understanding your requirements correctly, you want to have methods on each instance of the class that map (with a get/set prefix) to the keys in the data hash?
Although I despise magical logic in classes, could you not just define #method_missing and handle getting the value from the data hash based on that?
def method_missing method_symbol, *args, &block
if method_symbol.to_s.match(/^get_(.+)$/) && data.keys.include?($1.to_sym)
# Using the matching key in the data hash
data[$1.to_sym]
else
# Cannot detect method for current class, bubble method_missing to super class
super
end
end
It should also be noted that when overrideing method missing, you should always override #respond_to? as well

Ruby/Rails convert string to a class attribute

Suppose I have a class Article, such that:
class Article
attr_accessor :title, :author
def initialize(title, author)
#title = title
#author= author
end
end
Also, variable atrib is a String containing the name of an attribute. How could I turn this string into a variable to use as a getter?
a = Article.new
atrib='title'
puts a.eval(atrib) # <---- I want to do this
EXTENDED
Suppose I now have an Array of articles, and I want to sort them by title. Is there a way to do the compact version using & as in:
col = Article[0..10]
sorted_one = col.sort_by{|a| a.try('title') } #This works
sorted_two = col.sort_by(&:try('title')) #This does not work
You can use either send or instance_variable_get:
a = Article.new 'Asdf', 'Coco'
a.pubic_send(:title) # (Recommended) Tries to call a public method named 'title'. Can raise NoMethodError
=> "Asdf"
# If at rails like your case:
a.try :title # Tries to call 'title' method, returns `nil` if the receiver is `nil` or it does not respond to method 'title'
=> "Asdf"
a.send(:title) # Same, but will work even if the method is private/protected
=> "Asdf"
a.instance_variable_get :#title # Looks for an instance variable, returns nil if one doesn't exist
=> "Asdf"
Shot answer to your extended question: no. The &:symbol shortcut for procs relies on Symbol#to_proc method. So to enable that behavior you'd need to redifine that method on the Symbol class:
class Symbol
def to_proc
->(x) { x.instance_eval(self.to_s) }
end
end
[1,2,3].map(&:"to_s.to_i * 10")
=> [10, 20, 30]
ActiveRecord instances have an attributes hash:
a = Article.new(title: 'foo')
#=> <#Article id: nil, title: "foo">
atrib = 'title'
a.attributes[atrib]
#=> "foo"
You can use order to get sorted objects from your database:
Article.order('title').first(10)
#=> array of first 10 articles ordered by title

Ruby - Ignore protected attributes

How can I tell Ruby (Rails) to ignore protected variables which are present when mass-assigning?
class MyClass < ActiveRecord::Base
attr_accessible :name, :age
end
Now I will mass-assign a hash to create a new MyClass.
MyClass.create!({:name => "John", :age => 25, :id => 2})
This will give me an exception:
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: id
I want it to create a new MyClass with the specified (unprotected) attributes and ignore the id attribute.
On the side note: How can I also ignore unknown attributes. For example, MyClass doesn't have a location attribute. If I try to mass-assign it, just ignore it.
Use Hash#slice to only select the keys you're actually interested in assigning:
# Pass only :name and :age to create!
MyClass.create!(params.slice(:name, :age))
Typically, I'll add wrapper method for params to my controller which filters it down to only the fields that I know I want assigned:
class MyController
# ...
def create
#my_instance = MyClass.create!(create_params)
end
protected
def create_params
params.slice(:name, :age)
end
end
Setting mass_assignment_sanitizer to :logger solved the issue in development and test.
config.active_record.mass_assignment_sanitizer = :logger
You can use strong_parameters gem, that will be in rails 4.
See the documentation here.
This way you can specify the params you want by action or role, for example.
If you want to get down and dirty with it, and dynamically let only a model's attributes through, without disabling ActiveModel::MassAssignmentSecurity::Errors globally:
params = {:name => "John", :age => 25, :id => 2}
MyClass.create!(params.slice(*MyClass.new.attributes.symbolize_keys.keys)
The .symbolize_keys is required if you are using symbols in your hash, like in this situation, but you might not need that.
Personally, I like to keep things in the model by overriding assign_attributes.
def assign_attributes(new_attributes, options = {})
if options[:safe_assign]
authorizer = mass_assignment_authorizer(options[:as])
new_attributes = new_attributes.reject { |key|
!has_attribute?(key) || authorizer.deny?(key)
}
end
super(new_attributes, options)
end
Use it similarly to :without_protection, but for when you want to ignore unknown or protected attributes:
MyModel.create!(
{ :asdf => "invalid", :admin_field => "protected", :actual_data => 'hello world!' },
:safe_assign => true
)
# => #<MyModel actual_data: "hello world!">

Metaprogramming in Ruby On Rails

I have an array of strings: ["users", "torrents", "comments"]
these strings are the names of my bd tables.
how can I in .each loop connect to these tables and select|insert some data?
Avoid using eval
here is a simple solution using constantize
note: constantize will not allow arbitrary code to evaluated, it will just try to fetch a ruby constant, namely a Class
["users", "torrents", "comments"].each do |table_name|
# "users" => "User"
# or more complex
# "some_models" => "SomeModel"
#
class_name = table_name.singularize.camelize
# "User" => User
model_class = class_name.constantize
# do something with it
model_class.create!(:value => 12345)
end

Question for Ruby Gurus (help with Enum-like implementation)

I am trying to make a sort of "enum". Here is my implementation:
# Format of input hash to AnEnum::initialize is :
# {
# Symbol => [Fixnum => String]
# }
# Example:
# {
# :active => [1 => "Active"]
# }
class AnEnum
##values = nil
def initialize(hash)
##values = hash
end
def values
##values
end
def [](symbol)
values[symbol][0] # return the number for the symbol. e.g. 1
end
def text(symbol)
values[symbol][1] # return the text for the symbol. e.g. "Active"
end
end
Example Usage:
class MyClass1
##status = AnEnum.new({
:open => [1, 'Active'],
:closed => [2, 'Closed']
})
def self.Status
##status
end
end
# test it (it works!)
MyClass1.Status[:open] # => 1
MyClass1.Status.text(:open) # => "Active"
This works, but I want to make it more "elegant" and "dynamic" :
Is it possible to define AnEnum in MyClass2 like this:
class MyClass2
define_enum "Status", :as => {
:open => [1, 'Active'],
:closed => [2, 'Closed']
}
end
So that these will work:
MyClass2.Status[:open] # => 1
MyClass2.Status.text(:open) # => "Active"
Thus, the ##status and self.Status defined in MyClass1 above are automatically included in the class by the "macro"-like call to define_enum.
define_enum is intended to be working like for example the before_filter call in Rails.
Is this possible??
That's great if you're tackling this problem for your own personal gain, however if it's because you actually need this functionality, there are tons of Ruby gems out there that already do this. If you need each "State" to exhibit different behavior, I have written a useful gem called classy_enum. Otherwise, here are a ton of others.
To answer your question though, yes it is definitely possible to add class methods or macros as you are describing. A high level overview would look something like:
module MyEnum
def define_enum(name, states)
... meta code here ...
end
end
Then in your class:
MyClass
extend MyEnum
define_enum :name, :state1 => [], :state2 => []
end
The "meta code" is where it gets tricky depending on what you are trying to do. If you are going to go this route, I would still recommend checking out how others have done it first. You've got a few things in your example that are a little odd, such as capitalized method names (def self.Status) and class variables ##my_var.
Look at this: http://code.dblock.org/ShowPost.aspx?id=184 (slight improvement over http://www.rubyfleebie.com/enumerations-and-ruby/). Lets you write the following.
class Gender
include Enum
Gender.define :MALE, "male"
Gender.define :FEMALE, "female"
end
And of course
Gender.all
Gender::MALE

Resources