Ruby on Rails & Calling methods with Symbols Basic Question - ruby

For some reason I haven't quite gotten the hang of how Rails interacts with Ruby / figured out Ruby itself.
I'll get to the point. For example in a Ruby on Rails project you might have something like this:
class Product < ActiveRecord::Base
default_scope :order => 'title'
end
This confuses the crap out of me. I assume we are calling the method default_scope which Product inherits from the base ActiveRecord class... so that we can set some options. We pass it the symbol :order => 'title'. is :order just a hash value within the default_scope function and it is setting that hash value as 'title' ? Am I getting that correctly.
Also for example, when you start to throw basic validation in you get something like this
validates :price, :numericalcity => {:greater_than_or_equal_to => 0.01 }
I know what this does but its syntax blows my mind. First it looks like symbols are used for static reused string values, but here we are sending in a dynamic symbol in... where is that going? And then are we a symbol within a symbol? Is that basically a hash within a hash or what exactly is it doing? I'm just trying to trace it out within my brain to actually understand what is going on.

You are correct in assuming default_scope being a method, which is inherited from ActiveRecord::Base. Go here to view the source code of default_scope.
It is a method which takes an optional Hash as it's only parameter.
This,
default_scope :order => 'title'
is the same as writing it as,
default_scope( { :order => 'title' } )
In ruby if a method is defined like,
def foobar(options = {})
p options
end
But beware of a subtle difference in syntax. Above, if you leave out the () keeping the {} ruby understands it as something entirely different. Ruby sees a method default_scope which accepts a code block as it's argument.
default_scope { # code block }
This method definition will look like,
def foobar(&block)
yield
end
To understand how ruby blocks work read this.
You can call it like,
foobar :key_1 => 'value_1', "key_2" => 'value_2'
And Ruby understands it to be,
foobar( { :key_1 => 'value_1', "key_2" => 'value_2' } )
The keys of the Hash may or may not be symbols.
As for the validation helper method for the column attribute price,
validates :price, :numericality => { :greater_than_or_equal_to => 0.01 }
Is the same as,
validates( :price, { :numericality => { :greater_than_or_equal_to => 0.01 } } )
This is similar to a method definition like,
def validates(column_attr, options = {})
# perform validation of column_attr
end

default_scope :order => 'title'
Is a method call
default_scope( {:order => 'title'} )
Ruby allows you to omit parentheses and braces in this case.
validates :price, :numericalcity => {:greater_than_or_equal_to => 0.01 }
means
validates( :price, {:numericalcity => {:greater_than_or_equal_to => 0.01 } } )

Related

How to define method names and object references before actually having to use them (Ruby)

Say I am keeping track of email correspondances. An enquiry (from a customer) or a reply (from a supporter) is embedded in the order the two parties are corresponding about. They share the exact same logic when put into the database.
My problem is that even though I use the same logic, the object classes are different, the model fields I need to call are different, and the method names are different as well.
How do I put methods and objects references in before I actually have to use them? Does a "string_to_method" method exists or something like that?
Sample code with commentaries:
class Email
include Mongoid::Document
field :from, type: String
field :to, type: String
field :subject, type: String
belongs_to :order, :inverse_of => :emails
def start
email = Email.create!(:from => "sender#example.com", :to => "recipient#example.com", :subject => "Hello")
from_or_to = from # This represents the database field from where I later on will fetch the customers email address. It is either from or to.
enquiries_or_replies = enquiries # This represents a method that should later be called. It is either enquiries or replies.
self.test_if_enquiry_or_reply(from_or_to, enquiries_or_replies)
end
def test_if_enquiry_or_reply(from_or_to, enquiries_or_replies)
order = Order.add_enquiry_or_reply(self, from_or_to, enquiries_or_replies)
self.order = order
self.save
end
end
class Order
include Mongoid::Document
field :email_address, type: String
has_many :emails, :inverse_of => :order
embeds_many :enquiries, :inverse_of => :order
embeds_many :replies, :inverse_of => :order
def self.add_enquiry_or_reply(email, from_or_to, enquiries_or_replies)
order = Order.where(:email_address => email.from_or_to).first # from_or_to could either be from or to.
order.enquiries_or_replies.create!(subject: email.subject) # enquiries_or_replies could either be enquiries or replies.
order
end
end
Judging by the question and the code sample, it sounds like you are mixing concerns too much. My first suggestion would be to re-evaluate your method names and object structure. Ambiguous names like test_if_thing1_or_thing2 and from_or_to (it should just be one thing) will make it very hard for others, and your future self, to understand the code laster.
However, without diverging into a debate on separation of concerns, you can change the methods you call by using public_send (or the private aware send). So you can do
order.public_send(:replies).create!
order.public_send(:enquiries).create!
string to method does exist, it's called eval
so, you could do
method_name = "name"
eval(method_name) #calls the name method

How can I serialize DataMapper::Validations::ValidationErrors to_json in Sinatra?

I'm developing a RESTful API using Sinatra and DataMapper. When my models fail validation, I want to return JSON to indicate what fields were in error. DataMapper adds an 'errors' attribute to my model of type DataMapper::Validations::ValidationErrors. I want to return a JSON representation of this attribute.
Here's a single file example (gotta love Ruby/Sinatra/DataMapper!):
require 'sinatra'
require 'data_mapper'
require 'json'
class Person
include DataMapper::Resource
property :id, Serial
property :first_name, String, :required => true
property :middle_name, String
property :last_name, String, :required => true
end
DataMapper.setup :default, 'sqlite::memory:'
DataMapper.auto_migrate!
get '/person' do
person = Person.new :first_name => 'Dave'
if person.save
person.to_json
else
# person.errors - what to do with this?
{ :errors => [:last_name => ['Last name must not be blank']] }.to_json
end
end
Sinatra::Application.run!
In my actual app, I'm handling a POST or PUT, but to make the problem easy to reproduce, I'm using GET so you can use curl http://example.com:4567/person or your browser.
So, what I have is person.errors and the JSON output I'm looking for is like what's produced by the hash:
{"errors":{"last_name":["Last name must not be blank"]}}
What do I have to do to get the DataMapper::Validations::ValidationErrors into the JSON format I want?
So, as I was typing this up, the answer came to me (of course!). I've burned several hours trying to figure this out, and I hope this will save others the pain and frustration I've experienced.
To get the JSON I'm looking for, I just had to create a hash like this:
{ :errors => person.errors.to_h }.to_json
So, now my Sinatra route looks like this:
get '/person' do
person = Person.new :first_name => 'Dave'
if person.save
person.to_json
else
{ :errors => person.errors.to_h }.to_json
end
end
Hope this helps others looking to solve this problem.
I know, I am answering this late, but, in case you are just looking for just validation error messages, you can use object.errors.full_messages.to_json. For example
person.errors.full_messages.to_json
will result in something like
"[\"Name must not be blank\",\"Code must not be blank\",
\"Code must be a number\",\"Jobtype must not be blank\"]"
This will rescue on client side from iterating over key value pair.

Rendering a simple Ruby hash with RABL

I have a ruby hash that I'd like to render using RABL. The hash looks something like this:
#my_hash = {
:c => {
:d => "e"
}
}
I'm trying to render this with some RABL code:
object #my_hash => :some_object
attributes :d
node(:c) { |n| n[:d] }
but I'm receiving {"c":null}
How can I render this with RABL?
This works for arbitrary hash values.
object false
#values.keys.each do |key|
node(key){ #values[key] }
end
Worked for me using Rails 3.2.13 and Ruby 2.0.0-p195
Currently RABL doesn't play too nicely with hashes. I was able to work around this by converting my hash to an OpenStruct format (which uses a more RABL-friendly dot-notation). Using your example:
your_controller.rb
require 'ostruct'
#my_hash = OpenStruct.new
#my_hash.d = 'e'
your_view.rabl
object false
child #my_hash => :c do
attributes :d
end
results
{
"c":{
"d":"e"
}
}
Sometimes its easy to do too much imho.
How about just
render json: my_hash
And just like magic we can delete some code !
RABL deals in objects but does not require a particular ORM. Just objects that support dot notation. If you want to use rabl and all you have is a hash:
#user = { :name => "Bob", :age => 27, :year => 1976 }
then you need to first turn the hash into an object that supports dot notation:
#user = OpenStruct.new({ :name => "Bob", :age => 27, :year => 1976 })
and then within a RABL template treat the OpenStruct as any other object:
object #user
attributes :name, :age, :year
Consider that if everything you are doing in your app is just dealing in hashes and there is no objects or databases involved, you may be better off with an alternative more custom JSON builder such as json_builder or jbuilder.
Pasted from the official wiki page on RABL's github: https://github.com/nesquena/rabl/wiki/Rendering-hash-objects-in-rabl
RABL actually can render ruby hashes and arrays easily, as attributes, just not as the root object. So, for instance, if you create an OpenStruct like this for the root object:
#my_object = OpenStruct.new
#my_object.data = {:c => {:d => 'e'}}
Then you could use this RABL template:
object #my_object
attributes :data
And that would render:
{"data": {"c":{"d":"e"}} }
Alternatively, if you want :c to be a property of your root object, you can use "node" to create that node, and render the hash inside that node:
# -- rails controller or whatever --
#my_hash = {:c => {:d => :e}}
# -- RABL file --
object #my_hash
# Create a node with a block which receives #my_hash as an argument:
node { |my_hash|
# The hash returned from this unnamed node will be merged into the parent, so we
# just return the hash we want to be represented in the root of the response.
# RABL will render anything inside this hash as JSON (nested hashes, arrays, etc)
# Note: we could also return a new hash of specific keys and values if we didn't
# want the whole hash
my_hash
end
# renders:
{"c": {"d": "e"}}
Incidentally, this is exactly the same as just using render :json => #my_hash in rails, so RABL is not particularly useful in this trivial case ;) But it demonstrates the mechanics anyway.
By specifying a node like that, you are given access to the #my_hash object which you can then access attributes of. So I would just slightly change your code to be:
object #my_hash
node(:c) do |c_node|
{:d => c_node.d}
end
where c_node is essentially the #my_hash object. This should give you what you're expecting (shown here in JSON):
{
"my_hash":{
"c":{
"d":"e"
}
}
}
My answer is partially based on the below listed site:
Adapted from this site:
http://www.rubyquiz.com/quiz81.html
require "ostruct"
class Object
def to_openstruct
self
end
end
class Array
def to_openstruct
map{ |el| el.to_openstruct }
end
end
class Hash
def to_openstruct
mapped = {}
each{ |key,value| mapped[key] = value.to_openstruct }
OpenStruct.new(mapped)
end
end
Define this perhaps in an initializer and then for any hash just put to_openstruct and send that over to the rabl template and basically do what jnunn shows in the view.

DataMapper filter records by association count

With the following model, I'm looking for an efficient and straightforward way to return all of the Tasks that have 0 parent tasks (the top-level tasks, essentially). I'll eventually want to return things like 0 child tasks as well, so a general solution would be great. Is this possible using existing DataMapper functionality, or will I need to define a method to filter the results manually?
class Task
include DataMapper::Resource
property :id, Serial
property :name , String, :required => true
#Any link of type parent where this task is the target, represents a parent of this task
has n, :links_to_parents, 'Task::Link', :child_key => [ :target_id ], :type => 'Parent'
#Any link of type parent where this task is the source, represents a child of this task
has n, :links_to_children, 'Task::Link', :child_key => [ :source_id ], :type => 'Parent'
has n, :parents, self,
:through => :links_to_parents,
:via => :source
has n, :children, self,
:through => :links_to_children,
:via => :target
def add_parent(parent)
parents.concat(Array(parent))
save
self
end
def add_child(child)
children.concat(Array(child))
save
self
end
class Link
include DataMapper::Resource
storage_names[:default] = 'task_links'
belongs_to :source, 'Task', :key => true
belongs_to :target, 'Task', :key => true
property :type, String
end
end
I would like to be able to define a shared method on the Task class like:
def self.without_parents
#Code to return collection here
end
Thanks!
DataMapper falls down in these scenarios, since effectively what you're looking for is the LEFT JOIN query where everything on the right is NULL.
SELECT tasks.* FROM tasks LEFT JOIN parents_tasks ON parents_tasks.task_id = task.id WHERE parents_tasks.task_id IS NULL
You parents/children situation makes no different here, since they are both n:n mappings.
The most efficient you'll get with DataMapper alone (at least in version 1.x) is:
Task.all(:parents => nil)
Which will execute two queries. The first being a relatively simple SELECT from the n:n pivot table (WHERE task_id NOT NULL), and the second being a gigantic NOT IN for all of the id's returned in the first query... which is ultimately not what you're looking for.
I think you're going to have to write the SQL yourself unfortunately ;)
EDIT | https://github.com/datamapper/dm-ar-finders and it's find_by_sql method may be of interest. If field name abstraction is important to you, you can reference things like Model.storage_name and Model.some_property.field in your SQL.

Need explanation of some Ruby syntax

I am new to ruby on rails, could anybody explain what does the symbol ':' mean, what would be 'validates' and 'create_table'? So much confused...
class Post < ActiveRecord::Base
validates :name, :presence => true
validates :title, :presence => true, :length => {:minimum => 5}
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :name
t.string :title
t.text :content
t.timestamps
end
end
end
The colon character (:) is the beginning of a syntax literal for a Ruby "Symbol":
:abc.class # => Symbol
"abc".to_sym # => :abc
Symbols are like strings but they are "interned", meaning the Ruby interpreter only has a single copy of it in memory despite multiple possible references (whereas there can be many equivalent strings in memory at once).
The 'validates' token in your example above is a class method (of something in the class hierarchy of the "Post class") that is being called with a symbol argument (:name) and a hash argument with a single key/value pair of :presence => true.
The 'create_table' token is a method which is being called with a single argument (the symbol ":posts") and is given a block which takes a single argument "t" (do |t| ... end).
:foo is a symbol, i.e. a constant string that is guaranteed to be unique. They are often used in Ruby to reference fields or methods.
validates is used in ActiveRecord to set a constraint on field's values.
validates :name, :presence => true means that field name must be always set (not null, undefined or empty) for all instances of Post (and corresponding table in DB). validates :title, :presence => true, :length => {:minimum => 5} means that field title must be always set and its length must be greater than 5.
In Ruby, the : means that it is a Symbol. A Symbol is sort of like a lightweight string that's specifically used as an identifier. For example, in a hash, you use symbols as keys that point to their respective values.
my_hash = {:key_1 => "A", :key_2 => "B"}
In your examples above, you use symbols to specify the properties of your Post model and the columns of your posts table.
Here are a few links for further reading on Ruby symbols:
http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol
http://glu.ttono.us/articles/2005/08/19/understanding-ruby-symbols
http://www.ruby-doc.org/core/classes/Symbol.html
http://www.robertsosinski.com/2009/01/11/the-difference-between-ruby-symbols-and-strings/
:foo is a "symbol" which is essentially an immutable string. It's major advantage is the fact that it doesn't allocate a new object every time you use it. If you were to use the string "name" every time you needed to use it, you would be making a new String object every time. However, if you use :name instead, you are using the same Symbol object every time (same in terms of pointer equality and object identity).
validates and create_table are both methods. In ruby, a method doesn't need parenthesis when called, so validates :foo is the same as validates(:foo). The methods come via inheritance and module mixins. validates is a class method put onto ActiveRecord objects during the inheritance, and create_table is a instance method
Ruby Documentation and Ruby on Rails Documentation:
"what does the symbol : mean?" Class:Symbol
Symbol objects represent names and some strings inside the Ruby interpreter. They are generated using the :name and :"string" literals syntax, and by the various to_sym methods. The same Symbol object will be created for a given name or string for the duration of a program‘s execution, regardless of the context or meaning of that name. Thus if Fred is a constant in one context, a method in another, and a class in a third, the Symbol :Fred will be the same object in all three contexts.
"what would be validates?" ActiveModel::Validations::ClassMethods
This method is a shortcut to all default validators and any custom validator classes ending in ‘Validator’. Note that Rails default validators can be overridden inside specific classes by creating custom validator classes in their place such as PresenceValidator.
"what would be create_table?" ActiveRecord::ConnectionAdapters::SchemaStatements
Creates a new table
(The link shows examples, showing SQL statements generated by this method.)
#Grimm
As you should know, everything is object on RoR. There are cases where you need to differentiate String from others as the memory handling techniques of String are different from other data structures. Colon : is a form of consideration of such type. They're just symbols like the one that makes hash entry on the memory!
Get used to that, it'll be interesting!! :)
as everybody is saying, : is a start of a symbol. Symbol is simple a string or variable name for me. In your example, :name is a variable/symbol that reflect one of the field name in Post table. Rails automatically creates these symbols when you create a Model class.
In Ruby, you can call a method/function and specify their parameters with/without the brackets. So,
validates :name, :presence => true
can be written as
validates(:name, :presence => true)
So, you are actually passing the :name and the true as the parameters for validates method
hope this help you see clearer about the methods calling in Ruby.
Same as validates, create_table is also a method.
Apart from the symbols, the point to be noted here is that in Ruby, we dont have to implicitly give {} to specify that an argument is a hash if it is the last argument. I mean by calling
validates :name, :presence => true
you are calling
validates :name, {:presence => true}
or
validates(:name, {:presence => true})
then it becomes clear that you are calling a method validates with 2 arguments, a symbol and a hash. If we ignore the symbol and substitute strings in place, like this:
validates("name", {"presence" => true})
it is pretty much similar to a method call in any other language. So, watchout for this as it is used almost in every helper tags Rails use.
For the other methods also you can see this:
validates(:title, {:presence => true, :length => {:minimum => 5}})
In the case of create_table, it is a method call with 2 arguments, a symbol and a block.

Resources