I have a Message model that has the following relationships:
belongs_to :sender, Class: "User"
belongs_to :recipient, Class: "User"
I'm attempting to use class_eval to overwrite the recipient method in certain cases.
This works:
def update_recipient(message, recipient_addition = nil)
message.class_eval <<-EVAL
def recipient
"test"
end
EVAL
end
message.recipient => "test"
However, this doesn't:
def update_recipient(message, recipient_addition = nil)
message.class_eval <<-EVAL
def recipient
[#{message.recipient}, #{recipient_addition}]
end
EVAL
end
(eval):3: syntax error, unexpected keyword_end, expecting ']'
The first # is misinterpreted as a comment character, discarding the rest of the line. The #{} are expected to be interpolated inside double quotes, though there doesn't seem to be a reason to put these in #{} right now as they are just simple string values.
["#{message.recipient}", "#{recipient_addition}"]
... unless you're planning something like:
["To: #{message.recipient}", "CC: #{recipient_addition}"]
Related
I have the below code under test:
class MethodCache
##methods=Hash.new
def self.add_method(name, &block)
##methods[name]=block
end
def self.get_method(name)
##methods[name]
end
end
Now my spec looks like this:
describe MethodCache do
subject {MethodCache}
foo_block = ->{ puts "foo"}
it ".get_method" do
subject.add_method "foo", &foo_block
# does not work
# expect(subject.get_method("foo").to be &foo_block
# should syntax works
subject.get_method("foo").should be foo_block
end
end
I am trying to stay away from should syntax and use the expect syntax of RSpec. However it does not work in this case.
expect(subject.get_method("foo").to be &foo_block fails saying wrong number of arguments. I guess this is because the expectation block is treated as a block argument.
expect(subject.get_method("foo").to be foo_block (without the '&') does not work either. It says, the matcher expects a value and not argument.
What am I missing here?
I have two models;
class Foo
belongs_to :bar
end
class Bar
has 1, :foo
end
This all works fine, relationship working fine and so on. The requirement arose for us to override the "all" method on Foo, to always put a condition into any query. We did this like so;
class Foo
def self.all(opts = {})
super(opts.merge(:hidden => false))
end
end
And all that works too, but when I run the following command;
Foo.all.bar
It gives me the following error:
"condition :hidden does not map to a property in Bar"
That line worked totally fine before I overrode 'all'. I don't understand why it's applying "hidden" to the 'bar' object rather than the 'foo' object!
If anyone else encounters this, here is how I eventually fixed it. The problem was in the "query" object returned by datamapper - it holds a reference to a "model" and if that isn't the model your condition is on, you get the error I was getting. Basically the way around it is by doing a check against the model like so;
class << self
#Alias out the original all
alias :unfiltered_all :all
def all(opts = {})
query = unfiltered_all(opts)
if(query.model.ancestors.include? self)
return query.unfiltered_all(:hidden => false)
else
return query.all(:foo => { :hidden => false } )
end
end
end
I am working on a RoR project, and I want to know if I can use end as a method name. It seems to work fine, but I would like to know if this method will bring any issues in the future. I tried and it works:
class Dany
def end
puts 'Hola'
end
end
and this is the output:
Dany.new.end # => Hola
Ruby let's you do this, but you're going to run into all sorts of issues.
# end.rb
class Dany
def end
puts "Hola"
end
def other
end # should puts Hola
end
end
Instead, you will get
end.rb:10: syntax error, unexpected keyword_end, expecting end-of-input
Bottom line: don't do this. Don't use any keywords as a method name.
It is not a good idea to use a keyword as a method name, but as long as you disambiguate the token as a method call, you can use it. It is not practical though.
Dany.new.instance_eval{self.end} # => Hola
Dany.new.send(:end) # => Hola
Dany.new.method(:end).call # => Hola
Dany.new.instance_eval{end} # => syntax error, unexpected keyword_end
The usual disambiguation using () does not seem to work for this case, making it complicated.
Dany.new.instance_eval{end()} # => syntax error, unexpected keyword_end
I'm writing a few helpers to DRY up my tests. I pictured something like:
class ActiveSupport::TestCase
def self.test_presence_validation_of model, attribute
test "should not save #{model.to_s} with null #{attribute.to_s}", <<-"EOM"
#{model.to_s} = Factory.build #{model.to_sym}, #{attribute.to_sym} => nil
assert !#{model.to_s}.save, '#{model.to_s.capitalize} with null #{attribute.to_s} saved to the Database'
EOM
# Another one for blank attribute.
end
end
So that this:
class MemberTest < ActiveSupport::TestCase
test_presence_validation_of :member, :name
end
Executes exactly this at MemberTest class scope:
test 'should not save member with null name' do
member = Factory.build :member, :name => nil
assert !member.save, 'Member with null name saved to the Database'
end
Is it possible to do it this way (with a few adaptations, of course; I doubt my "picture" works), or do I have to use class_eval?
Have you seen Shoulda? It's great for testing common Rails functionality such as validations, relationships etc. https://github.com/thoughtbot/shoulda-matchers
In this case, it seems class_eval is necessary since I want to interpolate variable names into actual code.
Illustrated here.
I don't know how to create a ruby method that accepts a hash of parameters. I mean, in Rails I'd like to use a method like this:
login_success :msg => "Success!", :gotourl => user_url
What is the prototype of a method that accepts this kind of parameters? How do I read them?
If you pass paramaters to a Ruby function in hash syntax, Ruby will assume that is your goal. Thus:
def login_success(hsh = {})
puts hsh[:msg]
end
A key thing to remember is that you can only do the syntax where you leave out the hash characters {}, if the hash parameter is the last parameter of a function. So you can do what Allyn did, and that will work. Also
def login_success(name, hsh)
puts "User #{name} logged in with #{hsh[:some_hash_key]}"
end
And you can call it with
login_success "username", :time => Time.now, :some_hash_key => "some text"
But if the hash is not the last parameter you have to surround the hash elements with {}.
With the advent of Keyword Arguments in Ruby 2.0 you can now do
def login_success(msg:"Default", gotourl:"http://example.com")
puts msg
redirect_to gotourl
end
In Ruby 2.1 you can leave out the default values,
def login_success(msg:, gotourl:)
puts msg
redirect_to gotourl
end
When called, leaving out a parameter that has no default value will raise an ArgumentError
Use one single argument. Ruby will transform the named values into a hash:
def login_success arg
# Your code here
end
login_success :msg => 'Success!', :gotourl => user_url
# => login_success({:msg => 'Success!', :gotourl => user_url})
If you really want to make sure you get a hash, instead of the default ruby duck typing, then you would need to control for it. Something like, for example:
def login_success arg
raise Exception.new('Argument not a Hash...') unless arg.is_a? Hash
# Your code here
end