new to ruby on rails. have no idea on what's the difference on these two lines.
validation_code = ValidationCode.new { |v| v.email = params[:email], v.kind = 'sign_in', v.verify_code = code }
validation_code = ValidationCode.new email: params[:email], kind: 'sign_in', verify_code: code
ask this is because I'm getting two different result.
def create
code = SecureRandom.random_number.to_s[2..7]
validation_code = ValidationCode.new { |v| v.email = params[:email], v.kind = 'sign_in', v.verify_code = code }
# validation_code = ValidationCode.new email: params[:email], kind: 'sign_in', verify_code: code
if validation_code.save
render status: 200
else
render json:{errors: validation_code.errors}, status: 400
end
end
first written will return status 200, and second written will return status 400. Why?
Thanks
CodePudding user response:
So first, the first one is using the block form of ActiveRecord's initialize method, see more in the docs. There's no real difference to how each works, it's really just a style choice.
Your problem here is that the code you've included in your block isn't doing what you think it is because you're using commas to try to separate each statement. Ruby isn't seeing what you think it is, take a look at what v.email
equals after that assignment. It will be an array of the email AND the other two elements!
CodePudding user response:
This is how the difference looks visually:
# With keyword arguments
validation_code = ValidationCode.new email: params[:email], kind: 'sign_in', verify_code: code
validation_code.email == params[:email]
# => true
validation_code.kind == 'sign_in'
# => true
validation_code.verify_code == code
# => true
# With block
validation_code = ValidationCode.new { |v| v.email = params[:email], v.kind = 'sign_in', v.verify_code = code }
validation_code.email == [params[:email], 'sign_in', code].to_s
# => true
validation_code.kind == 'sign_in'
# => true
validation_code.verify_code == code
# => true
Because Ruby read it as
validation_code = ValidationCode.new { |v| v.email = [params[:email], v.kind = 'sign_in', v.verify_code = code] }
Pay attention that Rails do not just assign an array value to email
, but convert it to string
So in fact email is something like "[\"[email protected]\", \"sign_in\", 1234]"
If you want both lines to work the same, then the line with the block can be written as
validation_code = ValidationCode.new { |v| v.email = params[:email]; v.kind = 'sign_in'; v.verify_code = code }
or better multi-line
validation_code =
ValidationCode.new do |v|
v.email = params[:email]
v.kind = 'sign_in'
v.verify_code = code
end
CodePudding user response:
Both forms of new
do the same thing (though your code does not) just in different ways.
model = Model.new(key1: value1, key2: value2)
makes a new object, initializes it with the given values, and returns the new object.
model = Model.new { |m|
m.key1 = value1
m.key2 = value2
}
Makes a new object, and passes it to the block uninitialized. The block can now initialize the object in any way it pleases. Then the new, initialized object is returned.
The block form just offers more flexibility.
Why not just write this?
model = Model.new
model.key1 = value1
model.key2 = value2
If there is an after_initialize
callback it will be called after new
and before the object is initialized. That may cause problems because the object is not yet initialized.
But its really for create
:
model = Model.create
model.key1 = value1
model.key2 = value2
create
will call many callbacks on the uninitialized object, and it may violate database constraints such as not null
.
Back to your code: the problem is you've tried to separate statements with commas in order to cram it into one line.
validation_code = ValidationCode.new { |v|
v.email = params[:email], v.kind = 'sign_in', v.verify_code = code
^ ^
}
v.kind
and v.verify_code
are fine, but v.email
is an Array. You don't need the []
to make an Array. Ruby has parsed your code as:
v.email = [ params[:email], v.kind = 'sign_in', v.verify_code = code ]
Let's do the equivalent in IRB.
3.1.2 :001 > a = 4, b = 8, c = 12
=> [4, 8, 12]
3.1.2 :002 > a
=> [4, 8, 12]
3.1.2 :003 > b
=> 8
3.1.2 :004 > c
=> 12
3.1.2 :005 >
Instead, use semicolons to separate statements.
validation_code = ValidationCode.new { |v|
v.email = params[:email]; v.kind = 'sign_in'; v.verify_code = code
}
Better yet, use newlines for clarity.
validation_code = ValidationCode.new { |v|
v.email = params[:email]
v.kind = 'sign_in'
v.verify_code = code
}