field vs method ruby on rails - ruby

I have this class:
class User
include Mongoid::Document
field :revenues, :type => Integer, :default => nil
attr_accessible :revenues
#now method
def revenues
return 1
end
end
Why in console I get 1 instead nil?
1.9.3-p125 :002 > u.revenues
=> 1
Which has priority, the method or the field? How can I created a method with the same features that a field?

The field macro is defined in Mongoid::Document. It is neither a syntatic feature from Ruby nor from Rails.
What's happening with your code is the following:
The field function creates for you some methods, one of them is called revenues.
When you create another method called revenues, you are in effect overwriting the previously defined method, therefore making it useless.
Short answer: I don't understand a zip about Mongoid, but chances are that your field still exists even after you defined oce again a method named revenues. The only drawback is that you cannot access it by calling myUser.revenues anymore.
Try to make a test: access your field with the notation some_user[:revenues] and see what happen :)
Best regards

Related

Ruby Mongoid::Errors::InvalidField

class MyModule::MyModel
include Mongoid::Document
field :field1, :type=>Integer
...
field :fieldn, :type=>Integer
field :deleted, :type=>Boolean
store_in session: 'mydb', collection: 'mycollection'
end
These code threw Mongoid::Errors::InvalidField when came to :deleted definition. If I remove this line, it works well.
/var/lib/gems/2.1.0/gems/mongoid-4.0.0/lib/mongoid/fields/validators/macro.rb:56:in `block in validate_name': (Mongoid::Errors::InvalidField)`
As http://www.rubydoc.info/github/mongoid/mongoid/Mongoid/Errors/InvalidField says,
This error is raised when trying to create a field that conflicts with
an already defined method.
How can I use this conflicted name?
When I try to add a deleted field, Mongoid 4.0.2 says:
Problem:
Defining a field named 'deleted?' is not allowed.
Summary:
Defining this field would override the method 'deleted?', which would cause issues with expectations around the original method and cause extremely hard to debug issues. The original method was defined in:
...
When you say:
field :f
Mongoid creates three methods for that field: f (getter), f= (setter), and f? (is f truthy AFAIK). The last one is causing your problem because Mongoid has its own deleted? method.
Your best bet would be to use a different name for that field, field :is_deleted perhaps.
If you can't do that (i.e. you're attaching Mongoid to a predefined collection), then you could use dynamic attributes:
class MyModule::MyModel
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :field1, :type=>Integer
...
field :fieldn, :type=>Integer
# Don't define the field here
store_in session: 'mydb', collection: 'mycollection'
end
and then you'd access it use Mongoid's [] and []= methods:
d = MyModule::MyModel.new
d[:deleted] = true
d = MyModule::MyModel.find(id)
puts d[:deleted]
puts d.attributes['deleted']
You could also add your own is_deleted and is_deleted= methods that would use [] and []= to update the underlying attribute.

How to set a method dynamically as other class method

Im new to Ruby, and im creating a cli app with Thor and some additional gems. My problem is that i take user input (from the console) and pass the data as a variable to a existing method (This method is from a gem)
My method
def search(searchtype, searchterm)
search = OtherClass.new
result = search.search.searchtype keyword: "#{searchterm}"
puts result
# search.search.searchtype is not a method in the gem im using.
end
The OtherClass gem has these search methods: users, repos
The users method
def users(*args)
arguments(args, :required => [:keyword])
get_request("/legacy/user/search/#{escape_uri(keyword)}", arguments.params)
end
The repos method
def repos(*args)
arguments(args, :required => [:keyword])
get_request("/legacy/repos/search/#{escape_uri(keyword)}", arguments.params)
end
So how can i pass in the user data to the method from the OtherClass? Heres something like what i would want to do. The SEARCHTERM would be dynamically passed to the search.search object as a method parameter.
def search(SEARCHTYPE, searchterm)
search = OtherClass.new
result = search.search.SEARCHTYPE keyword: "#{searchterm}"
puts result
end
The "#{searchterm}" works as expected, but i also want to pass in the method to the search.search object dynamically, this could probably be done with if's but im sure theres a better way, maybe the Ruby way to solve this problem.
Finally i would want to be able to use this little program like this (the serch method)
./search.rb search opensource linux
(where opensource could be users, or another type of search, and linux could be the search keyword for the searchtype)
If this is possible i would apprechiate any help!
Thnx!
If you'd like to call a method dynamically, use Object#send.
http://ruby-doc.org/core-1.9.3/Object.html#method-i-send
I would caution against sending a method that was obtained by user input though, for security reasons.

Rails -- self vs. #

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
private
def encrypt_password
self.encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
I posted a previous question about before_save not working, and it turns out that what I had accidentally done is written my encrypt_password as:
def encrypt_password
#encrypted_password = encrypt(password)
end
I understand that if self.encrypted_password sets the encrypted_password attribute, but why does #encrypted_password not do that as well? In the response to the previous post about before_save not working someone said that the instance variable was "forgotten" after the method ended with the way I had originally coded it -- why was this the case? Can someone please explain how self and # work differently in the context of the code above?
NOTE: I already took a look at the posts here and here, but they both say that "self" is calling the attribute = method, and I don't even understand how that method could exist here since I never created it or declared the encrypted_password w/ attr_accessor. So I am still confused, and this is not a re-posting of those questions.
The accessors for encrypted_password have been automatically added by Rails for you because a field by that name exists in the users table.
Any field you add to a table will be automatically made available via self.field_name.
Here is where Michael Hartl's tutorial creates the encrypted_password field in the users table.
Also look at the user_spec.rb (Listing 7.3) in the linked page, where the author is testing for the presence of the encrypted_password field.
UPDATED:
As #mu points out, the # is used for Ruby instance variables (aka "iv"). But encrypted_password is an "attribute" defined by Rails, and is not an instance variable.
If you run User.find(1).instance_variables, you will see that there is an iv called #attributes, which is of type Hash.
Inside that iv is where the encrypted_password is stored. Rails has defined accessor methods for encrypted_password, which gets/sets the data for that
attribute in the #attributes Hash.
Note that you could also get/set the data via #attributes["encrypted_password"] called from within the User class (but the accessor methods are convenient way to do just that).
If you let me, I'd like to rephrase the answer.
I explained in this post, that as soon as you create a (rails-) Model with the same (singular) name as one of the (plural) tablenames of your database, the "magic" of rails will create setters and getters in order to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which defines basic CRUD accessors (Create, Read, Update, Delete).
The key point related to your question, is that you don't know how rails implements the instance variable related to your database table column, And you shouldn't. :) All you have to know is that at that point, you have setters and getters available to CRUD (create, read, update, delete) your database column "encrypted_password".
In your example, maybe rails uses an instance variable called #encrypted_password, maybe rails uses an hash-instance-variable called #attributes["encrypted_password"], or maybe rails uses an instance variable called #you_will_never_guess_encrypted_password.
-
And that's a good point you don't know about the internal rails behavior with instance variables. In 2019 Rails further development may lead the framework to use #complicated-hash-instance-variable to store the encrypted_password value.
In fact the best approach is to let rails manage its "private" "affair" ;) with instance variables, and just use the getter and setter methods it provides to you.
So your application will still work with encrypted_password in the next century (I hope so ^^).
So if you use #encrypted_password it may work with some "imaginary" version of rails and it won't work anymore with other rails versions. Actually with a current version of rails it doesn't work.
-
The second key point is that when you want to use the getter "encrypted_password" Rails created for your encrypted_password database table column, you prefix it with "self" in order to tells Ruby : "ok I want to use the encrypted_password method of my User instance variable."
In Ruby, a method is called by passing its name to a receiver.
You write it like this :
my_receiver.my_method
In your case we pass the method encrypted_password to the User instance variable. But we don't know how this instance variable will be named, so we use the word self to tell Ruby : "I'm talking about any instance variable of the User class that calls the encrypted_password method".
For instance we could have named our instance variable "toto" :
toto = User.new
so toto.encrypted_password would display the encrypted password, and self in this very case in our code would reference toto.
However, thanks to Ruby, if you don't give any receiver when calling a method, Ruby will assume you pass it to self.
Reference : Pragmatic Programmer's guide
So in your example, you even don't need to put "self." as prefix.
You could have it written like this :
class User < ActiveRecord::Base
def encrypt_password
encrypted_password = encrypt(password)
end
end
I hope this helps to clarify this interesting subject.
TL;DR -
Always write self.widget_count = 123 if you intend to save widget_count back to the database.
(But please do read the long answers, as the reason why is valuable to know.)

Is 'valid' a reserved name in DataMapper?

I have the following model in datamapper:
class Student
include DataMapper::Resource
property :id, Serial
# <snip>
property :permissions, String, :accessor => :protected, :required => true, :default => 'standard'
property :valid, Boolean, :default => false, :required => true
# <snip>
end
After requiring 'dm-validations' (version 1.1.0), and starting my Sinatra app, I recieve the following message:
/Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:81:in `valid?': wrong number of arguments (1 for 0) (ArgumentError)
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:81:in `save_self'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1007:in `_save'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1223:in `run_once'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:1006:in `_save'
from /Library/Ruby/Gems/1.8/gems/dm-core-1.1.0/lib/dm-core/resource.rb:406:in `save'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:69:in `save'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations/support/context.rb:30:in `validation_context'
from /Library/Ruby/Gems/1.8/gems/dm-validations-1.1.0/lib/dm-validations.rb:69:in `save'
<snip>
Is the 'valid' name I'm using for my model a reserved word? If it is, where can I find these words. I'm to the point of going on to trying to name it something like: 'student_valid' but now i'm just really curious about this.
Thanks
#valid? is a method that dm-validations adds. You cannot use "valid" as a property name because it automatically defines "valid?" method for a boolean property type which overrides dm-validations' valid?. Hence the error.
That's a tricky situation, I guess we need to improve the way we validate property names. Thanks for reporting this.
Well the way datamapper works, is that it uses method_missing at the end of the method call chain and finds your property. If there is a method with this same name then that is called rather than your property. Datamapper mixes in Validatable which has the method valid? Most of the time you learn what is reserved (Like all Object methods etc.) But if you want a full list you can do:
`myinstance.methods`
Anything that appears there will get called first.

Get a class by name in Ruby?

Having a string with the module and name of a class, like:
"Admin::MetaDatasController"
how do I get the actual class?
The following code works if there's no module:
Kernel.const_get("MetaDatasController")
but it breaks with the module:
ruby-1.8.7-p174 > Kernel.const_get("Admin::MetaDatasController")
NameError: wrong constant name Admin::MetaDatasController
from (irb):34:in `const_get'
from (irb):34
ruby-1.8.7-p174 >
If you want something simple that handles just your special case you can write
Object.const_get("Admin").const_get("MetaDatasController")
But if you want something more general, split the string on :: and resolve the names one after the other:
def class_from_string(str)
str.split('::').inject(Object) do |mod, class_name|
mod.const_get(class_name)
end
end
the_class = class_from_string("Admin::MetaDatasController")
On the first iteration Object is asked for the constant Admin and returns the Admin module or class, then on the second iteration that module or class is asked for the constant MetaDatasController, and returns that class. Since there are no more components that class is returned from the method (if there had been more components it would have iterated until it found the last).
ActiveSupport provides a method called constantize, which will do this. If you are on Rails, which I assume you are based on the name of your constant, then you already have ActiveSupport loaded.
require 'active_support/core_ext/string'
class Admin
class MetaDatasController
end
end
"Admin::MetaDatasController".constantize # => Admin::MetaDatasController
To see how the method is implemented, check out https://github.com/rails/rails/blob/85c2141fe3d7edb636a0b5e1d203f05c70db39dc/activesupport/lib/active_support/inflector/methods.rb#L230-L253
In Ruby 2.x, you can just do this:
Object.const_get('Admin::MetaDatasController')
=> Admin::MetaDatasController
i could be way off-base, but wouldn't eval return the class?
eval("Admin::MetaDatasController")
so eval("Admin::MetaDatasController").new would be the same as Admin::MetaDatasController.new

Resources