#988 new
Tim Griffin

HoboFields 1.3 needs updating to handle (legacy) field names containing spaces

Reported by Tim Griffin | December 16th, 2011 @ 06:37 PM

In using HoboFields against a legacy database that included a field named "Inuktitut Name", the following error was generated:

../hobo_fields-1.3.0/lib/hobo_fields/extensions/active_record/attribute_methods.rb:34:in `module_eval': 
../gems/hobo_fields-1.3.0/lib/hobo_fields/extensions/active_record/attribute_methods.rb:34: 
formal argument cannot be a constant (SyntaxError)
def Inuktitut Name; missing_attribute('Inuktitu...                  ^

Upon consultation with Bryan, it seems that HoboFields' attribute_methods.rb needs a little updating to current ActiveRecord code to handle this case.

Patching the define_read_method in attribute_methods.rb with code from ActiveRecord 3.0.11 seems to deal with this specific error, but it appears that Ruby itself still defines a method name containing a space (making it necessary to call the method using send as in (m.send(:"Inuktitut Name")). As such, I've added a line to remove spaces from the method name being defined, and to downcase it.

Not sure if this is an acceptable solution.

Note: The "symbol" reference in HoboField's original version was changed to "method_name" in the code below.


ActiveRecord::Base.class_eval do
  class << self

    def can_wrap_with_hobo_type?(attr_name)
      if connected?
        type_wrapper = try.attr_type(attr_name)
        type_wrapper.is_a?(Class) && type_wrapper.not_in?(HoboFields::PLAIN_TYPES.values)
      else
        false
      end
    end

    # Define an attribute reader method.  Cope with nil column.
    def define_read_method(method_name, attr_name, column)

      cast_code = column.type_cast_code('v') if column
      access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"

      unless attr_name.to_s == self.primary_key.to_s
        access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
      end
      
      # This is the Hobo hook - add a type wrapper around the field
      # value if we have a special type defined
      if can_wrap_with_hobo_type?(method_name)
        access_code = "val = begin; #{access_code}; end; wrapper_type = self.class.attr_type(:#{attr_name}); " +
            "if HoboFields.can_wrap?(wrapper_type, val); wrapper_type.new(val); else; val; end"
      end

      if cache_attribute?(attr_name)
        access_code = "@attributes_cache['#{attr_name}'] ||= begin; #{access_code}; end;"
      end

      # Where possible, generate the method by evalling a string, as this will result in
      # faster accesses because it avoids the block eval and then string eval incurred
      # by the second branch.
      #
      # The second, slower, branch is necessary to support instances where the database
      # returns columns with extra stuff in (like 'my_column(omg)'). 
      
      new_method_name = method_name.gsub(' ', '_').downcase
            
      if new_method_name =~ /^[a-zA-Z_]\w*[!?=]?$/
        generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
          def _#{new_method_name}
            #{access_code}
          end

          alias #{new_method_name} _#{new_method_name}
        STR
      else
        generated_attribute_methods.module_eval do
          define_method("_#{new_method_name}") { eval(access_code) }
          alias_method(new_method_name, "_#{new_method_name}")          
        end
      end
    end

    def define_method_attribute=(attr_name)
      if can_wrap_with_hobo_type?(attr_name)
        src = "begin; wrapper_type = self.class.attr_type(:#{attr_name}); " +
          "if !new_value.is_a?(wrapper_type) && HoboFields.can_wrap?(wrapper_type, new_value); wrapper_type.new(new_value); else; new_value; end; end"
        generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', #{src}); end", __FILE__, __LINE__)
      else
        super
      end
    end

  end
end

No comments found

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

People watching this ticket

Pages