Where to put and how to use modules for models

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.

James Byrne wrote:

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?

I followed this blog post for mixing in Class level methods "http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html"

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 Koch wrote:

I followed this blog post for mixing in Class level methods "http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html"

The blog you were looking for was not found.

Thanks. I will try moving the file as you suggest.

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:

Andy Koch 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.

James Byrne 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?

Hi --

James Byrne 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

[...]

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

James Byrne 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 --

James Byrne 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

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" :slight_smile: 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

James Byrne wrote:

James Byrne 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?

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.