I have written two helper methods that I wish to have these available in
all of my models. I have placed them in a module called
prep_index_string.rb which looks like this:
module PrepIndexString
This method prepares descriptive strings that are used as
indices for lookups. The keycase strips method leading and
trailing spaces, squeezes out extra whitespace between words,
and then downshifts all.
Do not use ! methods as they return nil under certain circumstances
If the attribute passed does not respond to the to_s message then
throw
an error. There is nothing useful we can do here.
def keycase(value)
value = value.to_s.strip.squeeze(" ").downcase
end
This method simply upshifts the first character of every element
separated by a word boundary in a string.
def titlecase(value)
value = value_to_s.gsub(/\b\w/){$&.upcase}
end
end
How and where is the best way to mixin these two methods for every model
(and possibly every view and controller as well) in a particular rails
project? I initially added these to application_helper.rb but have
since discovered that this is completely ignored by models. I tried
requiring “prep_index_string” and including/extending PrepIndexString at
the top of the model files but I nonetheless threw undefined method
errors on both methods whatever I tried.
It almost seems to me as if I should subclass String and mix them in
there but how should I do this for just one Rails project?
James B. wrote:
separated by a word boundary in a string.
requiring “prep_index_string” and including/extending PrepIndexString at
the top of the model files but I nonetheless threw undefined method
errors on both methods whatever I tried.
It almost seems to me as if I should subclass String and mix them in
there but how should I do this for just one Rails project?
I followed this blog post for mixing in Class level methods
“Ruby, iOS, and Other Development: Mixing in Class Methods”
but in your case, couldn’t you just drop your module in “lib” and
“include YourModule” in your model classes?
that’s what I would try
Andy K. wrote:
I followed this blog post for mixing in Class level methods
“Ruby, iOS, and Other Development: Mixing in Class Methods”
The blog you were looking for was not found.
Thanks. I will try moving the file as you suggest.
2008/3/13, James B. [email protected]:
the top of the model files but I nonetheless threw undefined method
errors on both methods whatever I tried.
Here’s the relevant chapter in the old Pickaxe:
http://ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
Put prep_index_string.rb in the lib/ directory. Then include the module
in all the classes you want to use the methods:
require "prep_index_string"
class SomeModelOrController
include PrepIndexString
# blah blah
end
This makes the methods available at the instance level.
If you want to have them at the class level too, add the line
“extend PrepIndexString” near the include line.
If you don’t wan’t to manually include it in every class you can
go the hardcore route and extend ActionController::Base
and ActiveRecord::Base.
Put this in lib/prep_index_string_extension.rb:
require "prep_index_string"
class ActiveRecord::Base
include PrepIndexString
extend PrepIndexString
end
class ActionController::Base
include PrepIndexString
extend PrepIndexString
end
and require “prep_index_string_extension” in environment.rb.
And make all the methods in PrepIndexString private.
You might also consider putting it in a plugin.
Stefan
another solution :
Buckblog: Concerns in ActiveRecord
In short : place your model modules in a dedicated directory
(…/concerns) that you add to the load path.
1: file config/environement.rb:
…
tell Rails how to find your modules :
config.load_paths += %W( #{RAILS_ROOT}/app/models/concerns )
…
2: one such shared module :
in app/models/concerns/super_powers.rb
module SuperPowers
…
end
3: and finally, in your model
class People << AR
include SuperPowers
…
end
Alain
Andy K. wrote:
but in your case, couldn’t you just drop your module in “lib” and
“include YourModule” in your model classes?
that’s what I would try
]$ ll lib/*rb
-rw-rw-r-- 1 byrnejb byrnejb 728 Mar 13 14:43 lib/prep_index_string.rb
$ cat lib//prep_index_string.rb
module PrepIndexString
This method prepares descriptive strings that are used as
indices for lookups. The keycase strips method leading and
trailing spaces, squeezes out extra whitespace between words,
and downshifts all.
Do not use ! methods as they return nil under certain circumstances
If the attribute passed does not respond to the to_s message then
throw
an error. There is nothing useful we can do here.
def keycase(value)
value = value.to_s.strip.squeeze(" ").downcase
end
This method simply upshifts the first character of every element
separated by a word boundary in a string.
def titlecase(value)
value = value_to_s.gsub(/\b\w/){$&.upcase}
end
end
$ cat app/models/entity.rb
require ‘prep_index_string’
class Entity < ActiveRecord::Base
include PrepIndexString
has_one :client
before_save :set_index_values
validates_presence_of :entity_name
validates_presence_of :entity_legal_name
validates_presence_of :entity_legal_form
def set_index_values
self[:entity_name] = self.entity_name.keycase
end
end
NoMethodError in ClientsController#create
undefined method `keycase’ for “A test name”:String
I do not know what I am doing wrong.
James B. wrote:
In console this works:
class Entity < ActiveRecord::Base
before_save :keycase
def keycase
self.entity_name = self.entity_name.to_s.strip.squeeze(" ").downcase
end
end
and this works:
class Entity < ActiveRecord::Base
before_save :set_index_values
def set_index_values
#self[:entity_name] = self.entity_name.keycase
keycase
end
def keycase
self.entity_name = self.entity_name.to_s.strip.squeeze(" ").downcase
end
But his does not:
class Entity < ActiveRecord::Base
before_save :set_index_values
def set_index_values
self.entity_name = self.entity_name.keycase
end
def keycase( value )
value.to_s.strip.squeeze(" ").downcase
end
end
$ ruby script/console
Loading development environment (Rails 2.0.2)
load “entity.rb”
=> [“Entity”]
en = Entity.new
=> #<Entity id: nil, entity_name: nil, entity_legal_name: nil,
entity_legal_form: nil, created_at: nil, updated_at: nil>
en.entity_name = “UPERAND”
=> “UPERAND”
en.entity_legal_name = “UperAnd Ltd.”
=> “UperAnd Ltd.”
en.entity_legal_form = “PERS”
=> “PERS”
en.save
NoMethodError: undefined method keycase' for "UPERAND":String from /home/byrnejb/Software/Development/Projects/invert/app/models/entity.rb:27:in
set_index_values’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:307:in
send' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:307:in
callback’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:304:in
each' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:304:in
callback’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/callbacks.rb:212:in
create_or_update' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:1972:in
save_without_validation’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/validations.rb:934:in
save_without_transactions' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in
save’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in
transaction' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:80:in
transaction’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:100:in
transaction' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in
save’
from
/usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:120:in
rollback_active_record_state!' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:108:in
save’
from (irb):6
Can anytone tell me why?
James B. wrote:
I do not know what I am doing wrong.
Even if I put the keycase method into the entity model I still get the
same error!?
#require ‘prep_index_string’
class Entity < ActiveRecord::Base
include PrepIndexString
#has_one :person
has_one :client
#has_one :vendor
#has_many :channels
#has_many :contacts
#has_many :identities
#has_many :job_parties
#has_many :locations
before_save :set_index_values
validates_presence_of :entity_name
validates_presence_of :entity_legal_name
validates_presence_of :entity_legal_form
def set_index_values
self[:entity_name] = self.entity_name.keycase
end
def keycase(value)
value = value.to_s.strip.squeeze(" ").downcase
end
end
What is Rails doing?
David A. Black wrote:
self.entity_name is a string. You’re calling keycase on that string,
but strings don’t have a keycase method.
You’d have to do:
self.entity_name = keycase(self.entity_name)
if you’re writing keycase to take an argument.
Thank you. This explains why placing this in a module did not work
either.
Now my question becomes: where would I subclass String to add these two
methods for a specific rails project?
James B. wrote:
Now my question becomes: where would I subclass String to add these two
methods for a specific rails project?
I need to clarify this. What I want to accomplish is to add these two
methods to the String class, but only for a particular project. What is
the recommended manner to accomplish this? I speculate that somewhere
in the load path I will need to create a class called String that
descends from String and adds these two methods. The question is where?
Class String
def keycase(value)
value = value.to_s.strip.squeeze(" ").downcase
end
def titlecase(value)
value = value_to_s.downcase.gsub(/\b\w/){$&.upcase}
end
end
Hi –
On Thu, 13 Mar 2008, James B. wrote:
descends from String and adds these two methods. The question is where?
end
If you want to add methods to the String class, you don’t need to
create a class; you just need to re-open the existing String class
(which is what you’ve done above, except you’ve capitalized “class”
Furthermore, if you’re defining the methods on the class, you
don’t want them to take an argument:
class String
def keycase
strip.squeeze(" ").downcase
end
end
Typically you would put a modification like this in the lib directory,
and require the file at runtime, or you’d put it in a file in the
config/initializers directory, in which case it would be read and
loaded automatically.
Note that Rails already adds titlecase to String.
David
–
Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!
James B. wrote:
James B. wrote:
Now my question becomes: where would I subclass String to add these two
methods for a specific rails project?
or should I do something like this instead?
String.class_eval do
def keycase
self.to_s.strip.squeeze(" ").downcase
end
def titlecase
self.to_s.downcase.gsub(/\b\w/){$&.upcase}
end
end
But again, where do I put this?
Hi –
On Thu, 13 Mar 2008, James B. wrote:
self.entity_name = self.entity_name.to_s.strip.squeeze(" ").downcase
#self[:entity_name] = self.entity_name.keycase
before_save :set_index_values
def set_index_values
self.entity_name = self.entity_name.keycase
end
def keycase( value )
value.to_s.strip.squeeze(" ").downcase
end
end
[…]
Can anytone tell me why?
self.entity_name is a string. You’re calling keycase on that string,
but strings don’t have a keycase method.
You’d have to do:
self.entity_name = keycase(self.entity_name)
if you’re writing keycase to take an argument.
David
–
Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!
David A. Black wrote:
Note that Rails already adds titlecase to String.
Which demonstrates yet again that if one wants to find out what is
already available, try to do it yourself first. Sigh.
Thanks.