Properties don't change when values are assigned? - ruby-on-rails-3

I'm a bit of Ruby, noob, and there's some basic thing I'm not getting. I have something like this:
def my_method
attr1 = 'new 1 value'
attr2 = 'new 2 value'
puts "#{attr1} - #{attr2}"
puts "Should be same? #{to_s}"
end
def to_s
"#{attr1} - #{attr2}"
end
When I call my_method I get:
new 1 value - new 2 value
Should be same? old 1 value - old 2 value
Huh?

This is because in Ruby
x = y
is always an assignment of the value resulting from y to the variable x while
obj.x = y
is always sending the x= message to the object obj (with the value resulting from y).
In Ruby attributes/properties are really just methods! Thus, try:
self.attr1 = 'new 1 value'
self.attr2 = 'new 2 value'
On the other hand, y may or may not be a method call (read: property fetch). It depends on if there is already a variable y in scope or not because variables shadow methods. This is why attr1 and attr2 work in to_s without needing to be prefixed.
Happy coding.

There are two ways to do it. One is to use class-scoped variables instead of local variables:
class MyClass
def my_method
#attr1 = 'new 1 value'
#attr2 = 'new 2 value'
puts "#{#attr1} - #{#attr2}"
puts "Should be same? #{self.to_s}"
end
def to_s
"#{#attr1} - #{#attr2}"
end
end
m = MyClass.new
m.my_method
Output:
new 1 value - new 2 value
Should be same? new 1 value - new 2 value
The other way is to use attributes, which you have to specifically call as methods on self:
class MyClass
attr_accessor :attr1,:attr2
def my_method
self.attr1 = 'new 1 value'
self.attr2 = 'new 2 value'
puts "#{attr1} - #{attr2}"
puts "Should be same? #{self.to_s}"
end
def to_s
"#{attr1} - #{attr2}"
end
end
m = MyClass.new
m.my_method
This has the same output.

It's the scope attr1 and attr2 are local variables.
So when you're calling to_s it looking for attr_1 and attr_2 that you've declared (probably) in the class scope. Those won't get overwritten when you run my_method instead you just created a new variable in a smaller scope.
Try using #attr_1 and #attr_2 instead.
Check out Local Variable Gotchas

Related

How to pass ActiveRecord model class to Resque

Let's say I have the following Resque job:
class Archive
#queue = :file_serve
def self.perform(repo_id, branch = 'master')
repo = Repository.find(repo_id)
repo.create_archive(branch)
end
end
What if I wanted to make this more generic by passing an object id and the object's class so that I can do something like this:
class Archive
#queue = :file_serve
def self.perform(object_class, object_id, branch = 'master')
object = object_class.find(object_id)
object.create_archive(branch)
end
end
This doesn't work, obviously, and I don't have a sense for what I should be doing, so if anyone can give some guidance, that would be really appreciated!
I would pass the name of the class to the job, rather than the class itself. Then you could call constantize on the name to get the class back, and call find on it. eg.
def self.perform(class_name, object_id, branch = 'master')
object = class_name.constantize.find(object_id)
object.create_archive(branch)
end

Ruby on rails if condition when adding new data to a sql table

I'm pretty new to ruby & ruby on rails and I have a little question :
I want to set a boolean to true if the value of the entry is higher than X and another boolean to true if this value if lower than Y.
I don't really know where to do the code for this or what's the best way to do it.
To be clear, I have a form(made with a scaffold) where I ask a value and depending on this value one of the 2 boolean might be set to true.
Thanks for your help!
you can put this on your controller. I would assume that the form is on controller#create where you would have:
boolean = true if #model.value > x
boolean2 = true if #model.value < y
then save it on db with #model.save
Assuming those booleans are attributes of the same model you have value on, it seems you could do this in before_save callback:
class SomeModel < ActiveRecord::Base
# Your X & Y with some example values
X_VALUE = 5
Y_VALUE = 10
before_save do
self.boolean1 = self.value > X_VALUE
self.boolean2 = self.value < Y_VALUE
true # callback needs to return true, otherwise save will fail
end
end
With above implementation even if you update the value, the proper boolean values will change as well. I hope this helps, as it's made on some assumptions. If not let me know, we'll figure something out.

How to implement Lua OOP in my example?

I am trying to create a class in Lua, which holds information about who is this class following, and I also want followed class to know about his follower.
But my code fails, and I dont know why.
The error is
lua: OOP.lua:90: attempt to call method 'getName' (a nil value)
Vehicle = {}
--------
--FIELDS
--------
Vehicle.name = ""
Vehicle.vtype = "";
Vehicle.follower = {};
Vehicle.leader = {};
---------------------
--SETTERS AND GETTERS
---------------------
function Vehicle:setName(value)
self.name = value
end
function Vehicle:getName()
return self.name
end
function Vehicle:setType(value)
self.vtype = value
end
function Vehicle:getType()
return self.vtype
end
function Vehicle:setFollower(pVeh)
self.follower = {};
self.follower = pVeh;
end;
function Vehicle:getFollower()
return self.follower;
end;
function Vehicle:getLeader()
return self.leader;
end;
function Vehicle:setLeader(pVeh)
self.leader = pVeh;
if (self.leader ~= nil) then
print ("-- setted leader! --");
end;
end;
-----------
--METHODS--
-----------
function Vehicle:follow(pVeh) --> why does this not work?
self:setLeader(pVeh);
pVeh:setFollower(self);
end;
-- constructor
function newVehicle(pNm,pTp)
tbl = {}
setmetatable(tbl, {__index = Vehicle});
tbl:setName(pNm);
tbl:setType(pTp);
return tbl
end
-----------------------------------------------
-- TEST
-----------------------------------------------
local car1 = newVehicle("Mazda","Car");
local car2 = newVehicle("Harley","Bike");
print("--------------");
print (car1:getName()); --> prints "Mazda"
print (car2:getName()); --> prints "Harley"
car2:follow(car1)
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
The problem is in your test code, from what I can see:
print ("Leader of " .. car1:getName() .. " is " .. car1:getLeader():getName())
car1 does not have a leader defined. (you did car2:follow(car1))
Some pointers:
You need to define field tables in the creation method. This won't apply to your code just yet, but say you wanted to do platoons. You'd do something like Vehicle.platoon = {}; Tank1.platoon[Tank2] = true -- This would add Tank2 to Vehicle's platoon table, and share it with every vehicle. If you define this in the new method: function newVehicle(...) tbl.platoon = {} ... end, then it would be unique to every vehicle.
Don't use accessor (get*, set*) methods unless you have a solid reason for using them. They add clutter, and are not good for performance. A good reason to use them would be if you are accessing a table in your accessor method, and want to simplify the interaction with that. (i.e Obj:addChild(Obj2) obj:hasChild(Obj2)) But then it probably doesn't qualify as an accessor anymore :)
You can use dummy objects for unset fields. This'll prevent errors like yours:
local Dummy = newVehicle("None", "None")
Vehicle.follower = Dummy
Vehicle.leader = Dummy
In your code, this produces:
--------------
Mazda
Harley
-- setted leader! --
Leader of Mazda is None
car2:follow(car1)
car1 is being followed by car2. Whereas, later; you're trying to access car1's leader, which, of course returns nil. Hence the error.
print( "Follower of "..car1:getName().." is "..car1:getFollower():getName() )

Rails - use object oriented "advanced"

I would like to maintain my code DRY, then I want to transform this pseudo-code:
def aMethod
a = aModel.find(2)
b = a.getVariable
a.setVariable = c
end
in something like this
def aMethod
anotherMethod(aModel, getVariable)
end
def anotherMethod(model, var)
a = model.find(2)
b = a.var
a.var = c
end
In my tests, seems that there is no problem for the model, but for the getVariable (i.e. accessing the variable of the model) it doesn't work: undefined local variable or method
Any ideas?
You likely want to use send, if I understand what you're trying to do, e.g.,
def anotherMethod(model, var_sym)
a = model.find(2)
b = a.send(var_sym)
a.send("#{var_sym}=", c)
end
anotherMethod(aModel, :getVariable)
(With the caveat that I don't know what a, b, or c are, or should do, since they're locals in the OP.)

Increment integer on save in Rails 3?

I have the following code in my Rails 3 application:
def like
#suggestion = Suggestion.find(params[:id])
Suggestion.update_all("votes = (votes + 1)")
redirect_to suggestions_url
end
def dislike
#suggestion = Suggestion.find(params[:id])
Suggestion.update_all("votes = (votes - 1)")
redirect_to suggestions_url
end
It's working, but rather than updating the current suggestion it's updating them all. So I changed it to:
def like
#suggestion = Suggestion.find(params[:id])
#suggestion.update_all("votes = (votes + 1)")
redirect_to suggestions_url
end
def dislike
#suggestion = Suggestion.find(params[:id])
#suggestion.update_all("votes = (votes - 1)")
redirect_to suggestions_url
end
but then I get:
undefined method `update_all' for #<Suggestion:0x007f87c2b918a0>
So then I tried #suggestion.update_attribute(:votes, '1') but that resets the value to 1 instead of incrementing it.
What's the correct way to achieve this? I just want the integer (votes) of the current suggestion to increment/decrease by 1 on each save.
I've also tried the following with no luck:
def like
#suggestion = Suggestion.find(params[:id])
#suggestion.increment(:votes)
redirect_to suggestions_url
end
This seems more suitable in the model. I suggest creating a like method inside the model like so:
def like
self.increment!(:votes)
end
Now you can do something like this:
#suggestion = Suggestion.find(params[:id])
#suggestion.like
Note: increment!, with the exclamation point also performs the save action
A couple things. It sounds like what you want is a controller action that increments an attribute by one. You were probably closest with the code
#suggestion.update_attribute(:votes, '1')
If you check the documentation for that method, it sets the value of the attribute votes to the second arguement, the string '1', on the object, #suggestion, and its corresponding row in the database. Instead of setting it to '1', you want to set it to the incremented value:
#suggestion.update_attribute(:votes, #suggestion.votes + 1)
Ethan suggested using the convenience method, increment!, which works just the same.
Now, if you wanted to actually auto-increment each time the object gets saved (as in something else about the object gets altered, you'd want to use the :before_save callback with increment without the bang.