Struggling to understand how this block works - ruby-on-rails-3

I am looking at factories in rails and have hit a problem in my understanding of this code:
Factory.define :user do |f|
f.username "foo"
f.password "foobar"
f.password_confirmation { |u| u.password }
end
I understand the mechanics of everything except for
f.password_confirmation { |u| u.password }
How would this know to assign "foobar" to f.password_confirmation in the case where I used "foobar" as a custom password. Or in other words what does 'u' reference. Thanks in advance.

The "u" in that case actually represents an "Evaluator" class, which is an internal proxy used by factory_girl. It's a dynamically defined class that responds to methods for the attributes you're defining on your factory.
The Evaluator allows you to access previously defined attribute values, and it will generate, cache, and return the correct value if the attributes are out of order. For example, swapping the order of "password" and "password_confirmation" will still work because of the way the Evaluator works.
You can see how Evaluator works here: https://github.com/thoughtbot/factory_girl/blob/master/lib/factory_girl/evaluator.rb
You mostly don't need to worry about the Evaluator when defining factories. You can generally "u" there like it's an instance of a User, because it will delegate missing methods to the instance that it's building.

Related

Implementing Form Objects in Hanami

In Uncle Bob's clean architecture, which Hanami is inspired by, Form Objects guard the boundary between Interactors and our delivery mechanism (typically an http endpoint).
In the Hanami docs, bounary guarding is done using params blocks in Actions (see here). This seems to couple validation to the http delivery mechanism. It seems more natural to me that Form Objects (or params black which accomplish the same thing) would live in delivery-mechanism-agnostic Interactors.
Unfortunately, I cannot figure out if Hanami supports such a design. I found a similar question on the Hanami forum, but it has no answer.
To clarify, below is a snippet of code demonstrating the kind of thing I want to do, but using Virtus and ActiveModel::Validations rather than Hanami facilities. For those familiar with Trailblazer, its contract block within its operations (its term for Interactor) is another example.
Finally, am I misunderstanding something about the intended use of Hanami? Hanami supports Interactors, so it seems like this should be possible...
require 'hanami/model'
require 'active_model'
require 'virtus'
class PersonForm
include Virtus.model
include ActiveModel::Validations
attribute :name, String
attribute :age, Integer
validates :name, :age, presence: true
def each
attributes.each {|a| yield a}
end
end
class Person
include Hanami::Entity
attributes :name, :age
end
# Code like this would then live inside of an Interactor, which
# can accept a params hash.
jonah_form = PersonForm.new({name: 'Jonah', age: '99'})
jonah = Person.new(jonah_form) if jonah_form.valid?
p jonah #=> <Person:0x007fbdde1edcc0 #id=nil #name="Jonah" #age=99>
# do stuff with our jonah entity
sorry for missing this question.
The params act as validator to save developers to create and instantiate another class. In Ruby Community this is a problem.
DSL to the rescue for this compromise.
If you prefer to have a concrete form object, instead of using Virtus and ActiveModel, you can just include Hanami::Validations and have the same behavior.

Rails + Devise - Session Controller explaination

I am playing around with Devise in a project, and am just trying to better understand how it all works. The Sessions controller in particular is doing a few things that I don't understand:
class Devise::SessionsController < ApplicationController
def new
# What benefit is this providing over just "resource_class.new"?
self.resource = resource_class.new(sign_in_params)
clean_up_passwords(resource)
# What is "serialize_options" doing in the responder?
respond_with(resource, serialize_options(resource))
end
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
...
protected
...
def serialize_options(resource)
methods = resource_class.authentication_keys.dup
methods = methods.keys if methods.is_a?(Hash)
methods << :password if resource.respond_to?(:password)
{ :methods => methods, :only => [:password] }
end
def sign_in_params
devise_parameter_sanitizer.sanitize(:sign_in)
end
end
I assume that these methods are adding some sort of security. I'd just like to know what exactly they are protecting against.
The implementation of devise_parameter_sanitizer is creating a ParameterSanitizer instance. I often find looking at tests to be helpful in understanding the purpose of code; and this test I think illustrates it best-- since you're creating a new user, you don't want to allow users to assign any value they want to any parameter they want, so sanitize means "strip out any attributes other than the ones we need for this action". If this wasn't here, and you had a role attribute, a user could send a specially-crafted POST request to make themselves an admin when signing up for your site. So this protects against what's called, in the general case, a mass assignment vulnerability. Rails 3 and Rails 4 protect against this in different ways but the protections can still be turned off, and I'm guessing Devise is trying to set some good-practice defaults.
The serialize_options method is creating a hash of options to support rendering to XML or JSON. I found this out by looking at the implementation of responds_with, which calls extract_options! which uses the last argument as options if the last argument is a Hash. The documentation for responds_with says "All options given to #respond_with are sent to the underlying responder", so I looked at ActionController::Responder, whose documentation explains the process it takes to look for a template that matches the format, then if that isn't found, calling to_#{format}, then calling to_format. There's a view for HTML, and ActiveRecord objects respond to to_xml and to_json. Those methods use those options:
The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
attributes included, and work similar to the +attributes+ method.
To include the result of some method calls on the model use <tt>:methods</tt>.
So this keeps you from exposing more information than you might want to if someone uses the XML or JSON format.

Rails 3: Validations for object

I'm creating an object via controller. A Foo has many Bars. The Bar model has a validation that it must have a Foo in order to be valid.
In the foo.rb model:
has_many: bars
In the bar.rb model:
validates_presence_of :foo
belongs_to: foo
In the foo_controller.rb:
#foo = Booking.new(params[:foo]) # this has all the params needed to make a Foo.
#foo.create_bars(params[:bars])
In the foo.rb model:
def create_bars(bars)
bars.each do |t|
bar = Bar.create({name: name, email: email, foo: foo})
bar.foo = self
self.bars << bar
bar.save
puts self.bars.to_s
end
end
The puts self.bars.to_s
This sounds like it should be something really basic, but since the foo doesn't exist in the db, does ActiveRecord consider it to be nil, and that's why it's not saving? How can I write this properly?
Maybe try this:
def create_bars(bars)
bars.each do |t|
self.bars << Bar.new({name: t[:name], email: t[:email]})
end
end
<< operator used for active record objects sets association automatically.
Do not forget to add has_many :foos, and belongs_to :bar in the models.
I'll answer this part first:
This sounds like it should be something really basic, but since the
foo doesn't exist in the db, does ActiveRecord consider it to be nil,
and that's why it's not saving? How can I write this properly?
Not quite. The validation doesn't depend on anything in the database, it merely checks whether the specified field (or dependent object) is present in the model before persisting. The create method is a kind-of shortcut that not only initializes an ActiveModel object, but also simultaneously attempts to save it in the database. Your validation is failing because you're calling this method BEFORE you've set the foo field of your bar object.
You can technically use create in this instance by passing foo: self in your scenario, but frankly I would go with #pablopablo89's answer in regards to this part. If you do it this way, when you save Foo, all the Bar objects will also get saved.
Additionally, the .create method for creating Bar objects is dangerous because, since you're immediately persisting Bar objects independent of the parent Foo, if for whatever reason your Foo cannot be saved (fails some other validation, etc etc), you end up with a bunch of orphaned Bar objects with no way to remove them independent of a Foo (I'm making a bit of an assumption of how your system actually works). Assuming this is a reflection of your system, the goal you want to keep in mind is that an object, and all of its dependencies, are saved in one atomic operation. If one fails, they all fail, and you alert the user.
To answer the question more generally, as #phoet pointed out in the comment, you might be a lot better off changing your form and using the accepts_nested_attributes_for method. Here is the link to the rails documentation.
In the Foo model, add the line accepts_nested_attributes_for :bars, and then use fields_for in the form, something like this:
<%= form_for #foo do |f| %>
...other stuff...
<%= f.fields_for :bars do |f_bar| %>
... bar-related stuff ...
The submitted parameter map will return with all the bar-related stuff within the :foo key in such a way that it will create both the Foo object, and all of the related Bar objects all at once. In that scenario, create will also work, although I still tend to do separate .new and .save, but its up to you.
This link regarding how to use fields_for might be helpful as well.
Booking.new just initializes an object. In order for foo to exist you need to save it. Once you save it your validation while creating bar objects will pass and the objects should be created. Hopefully this should work

How to properly scope User attributes in rails

How to achieve simple scoping in your views such as:
<% if #user.admin %>
where "admin" is the following scope in user.rb:
scope :admin, where(role: "admin")
there is a column Role which is a string in the Users table
I've done the same thing before with another Model (but not a devise user model) to which I could later call
<% if objective.completed %>
right after calling an each method in the objectives.
However
When I do the exact same thing to a user model I get an
undefined method `admin' for #<User:0x00000107e39038>
How could I make it work? I've been digging for hours.
For a no scope workaround try:
<% if #user.role == "admin" %>
You simply can't use scopes this way. Scopes are used as class methods, so if you run
User.admin
it returns list of users matching given condition. What you need is an instance method. Add it to your user.rb file:
def admin?
admin == 'admin'
end
and you will be able to use it in your view:
- if #user.admin?
anyways, you should definitely reconsider storing roles as string in users table. Try to create another table called roles.
Scopes are usually class level methods, having said that, how can you access it with an instance. I think, that is why the error says undefined method.

What is mass-assignment in Rails 3

I have heard couple of people complaining and posting questions about mass-assignment in Rails. I have got same error couple of times and all I did was attr_accessible. But what exactly is mass assignment? could somebody explain with example?
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the name and topic attribute of the Post model:
Post.new(:name => "John", :topic => "Something")
Post.create(:name => "John", :topic => "Something")
Post.update_attributes(:name => "John", :topic => "Something")
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
You have an attr_accessible declaration which does not include :name
You have an attr_protected which does include :name
It recently became the default that attributes had to be manually white-listed via a attr_accessible in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed attr_protected or any other attribute was white-listed with attr_acessible.
It is important to consider which attributes can be mass assigned because code like this is so common:
#post = Post.new(params[:post])
Typically this is used when the user submits a form rendered by a form_for #post. In an ideal world, the params[:post] hash should only contain the fields we displayed on the form. However, it is trivial easy for the user to pass additional fields in their request, so in effect you're allowing a user to set any fields on #post, not just the ones displayed on the form.
Failure to safely use mass assignment has led to several high profile bugs in some pretty big Rails applications, like the one that allowed somebody to inject their own public key into the list of trusted keys on a Github repository and push code directly to a repository they should not have had access to.