Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make sure getters always return Symbols #30

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

tomasc
Copy link

@tomasc tomasc commented Dec 16, 2015

We have ran into several issues when relying on enum to return Symbol type.

This PR replaces read_attribute by send, since the read_attribute returns unprocessed (raw) value before typecast (demongoize) and therefore can potentially return something else than Symbol.

Also, this PR makes sure all Array values (in multiple option) are Symbols as well.

For your reference, here you can see how Mongoid uses read_attribute to get raw value which is then processed to a proper type using demongoize: https://github.com/mongodb/mongoid/blob/master/lib/mongoid/fields.rb#L411-L424

This is extermely important especially now, when Symbols are being deprecated and an app using Mongoid can potentially start returning Strings instead of Symbols. See here: https://jira.mongodb.org/browse/RUBY-1075?jql=project%20%3D%20RUBY%20AND%20resolution%20%3D%20Unresolved%20ORDER%20BY%20priority%20DESC%2C%20key%20DESC

@@ -71,12 +71,12 @@ def define_field_accessor(name, field_name, options)

def define_array_field_accessor(name, field_name)
class_eval "def #{name}=(vals) self.write_attribute(:#{field_name}, Array(vals).compact.map(&:to_sym)) end"
class_eval "def #{name}() self.read_attribute(:#{field_name}) end"
class_eval "def #{name}() self.send(:#{field_name}).map{ |i| i.try(:to_sym) } end"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [90/80]

@onomated
Copy link

+1.
Currently running into this issue as I had to manually rename all my Mongo collections to remove underscores. This changed the field types to strings and breaks the <enum_val>? methods.
I got into this situation due to commit 2288ee6 which stopped using Mongoid's aliasing features, so I would've had to reference enum fields with underscore prefixes. Instead of changing to underscore prefixes, I changed the mongoid enum prefix configuration to empty string. As a result I had to rename the fields in the DB.

Monkey-patched in my project with this module. The solution in this PR resulted in stack overflow:

module CoreExtensions
  module Mongoid
    module Enum
      module EnsureSymbol
        extend ActiveSupport::Concern

        included do |base|
          base.instance_eval do
            def define_array_field_accessor(name, field_name)
              class_eval <<-EORUBY, __FILE__, __LINE__ + 1
                def #{name}=(vals)
                  self.write_attribute(:#{field_name}, Array(vals).compact.map(&:to_sym))
                end
                def #{name}()
                  self.read_attribute(:#{field_name}).map{ |i| i.try(:to_sym) }
                end
              EORUBY
            end

            def define_string_field_accessor(name, field_name)
              class_eval <<-EORUBY, __FILE__, __LINE__ + 1
                def #{name}=(val)
                  self.write_attribute(:#{field_name}, val.try(:to_sym))
                end
                def #{name}()
                  self.read_attribute(:#{field_name}).try(:to_sym)
                end
              EORUBY
            end
          end
        end

      end
    end
  end
end

Applied by:

Mongoid::Enum.include CoreExtensions::Mongoid::Enum::EnsureSymbol

@tomasc
Copy link
Author

tomasc commented Feb 16, 2016

any chance to have this merged?

tomasc and others added 2 commits February 24, 2016 14:25
* Refer to constant instead of passing around the values
validates field_name, :inclusion => {:in => values.map(&:to_sym)}, :allow_nil => !options[:required]
validates field_name, :'mongoid/enum/validators/multiple' => { :in => self.const_get(name.to_s.upcase), :allow_nil => !options[:required] }
elsif options[:validate]
validates field_name, :inclusion => {:in => self.const_get(name.to_s.upcase)}, :allow_nil => !options[:required]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line is too long. [122/80]
Use the new Ruby 1.9 hash syntax.
Redundant self detected.
Space inside { missing.
Space inside } missing.

@tomasc
Copy link
Author

tomasc commented Mar 26, 2016

can we have this merged please?

@jeremyhaile
Copy link

I'd question this change vs a change that converts everything to strings. The reason is that the "symbol" type is deprecated by BSON and is deprecated in Mongo 3.2.

I opened an issue specifically for this to discuss: #41

@tomasc
Copy link
Author

tomasc commented Oct 8, 2016

@jeremyhaile I am not quite sure whether changes on the db level should have impact on how the enum abstraction works in Ruby (unless of course there is deprecation of Symbols in Ruby itself). Perhaps a way forward might be to add config option (Symbol/String)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants