List dynamic attributes in a Mongoid Model

后端 未结 6 1361
醉酒成梦
醉酒成梦 2021-02-06 15:49

I have gone over the documentation, and I can\'t find a specific way to go about this. I have already added some dynamic attributes to a model, and I would like to be able to it

相关标签:
6条回答
  • 2021-02-06 16:23

    Just include something like this in your model:

    module DynamicAttributeSupport
    
      def self.included(base)
        base.send :include, InstanceMethods
      end
    
      module InstanceMethods
        def dynamic_attributes
          attributes.keys - _protected_attributes[:default].to_a - fields.keys
        end
    
        def static_attributes
          fields.keys - dynamic_attributes
        end
      end
    
    end
    

    and here is a spec to go with it:

    require 'spec_helper'
    
    describe "dynamic attributes" do
    
      class DynamicAttributeModel
        include Mongoid::Document
        include DynamicAttributeSupport
        field :defined_field, type: String
      end
    
      it "provides dynamic_attribute helper" do
        d = DynamicAttributeModel.new(age: 45, defined_field: 'George')
        d.dynamic_attributes.should == ['age']
      end
    
      it "has static attributes" do
        d = DynamicAttributeModel.new(foo: 'bar')
        d.static_attributes.should include('defined_field')
        d.static_attributes.should_not include('foo')
      end
    
      it "allows creation with dynamic attributes" do
        d = DynamicAttributeModel.create(age: 99, blood_type: 'A')
        d = DynamicAttributeModel.find(d.id)
        d.age.should == 99
        d.blood_type.should == 'A'
        d.dynamic_attributes.should == ['age', 'blood_type']
      end
    end
    
    0 讨论(0)
  • 2021-02-06 16:24

    I wasn't able to get any of the above solutions to work (as I didn't want to have to add slabs and slabs of code to each model, and, for some reason, the attributes method does not exist on a model instance, for me. :/), so I decided to write my own helper to do this for me. Please note that this method includes both dynamic and predefined fields.

    helpers/mongoid_attribute_helper.rb:

    module MongoidAttributeHelper
      def self.included(base)
        base.extend(AttributeMethods)
      end
    
      module AttributeMethods
        def get_all_attributes
          map = %Q{
              function() {
                for(var key in this)
                {
                  emit(key, null);
                }
              }
            }
    
          reduce = %Q{
              function(key, value) {
                return null;
              }
            }
    
          hashedResults = self.map_reduce(map, reduce).out(inline: true) # Returns an array of Hashes (i.e. {"_id"=>"EmailAddress", "value"=>nil} )
    
          # Build an array of just the "_id"s.
          results = Array.new
          hashedResults.each do |value|
            results << value["_id"]
          end
    
          return results
        end
      end
    end
    

    models/user.rb:

    class User
       include Mongoid::Document
       include MongoidAttributeHelper
       ...
    end
    

    Once I've added the aforementioned include (include MongoidAttributeHelper) to each model which I would like to use this method with, I can get a list of all fields using User.get_all_attributes.

    Granted, this may not be the most efficient or elegant of methods, but it definitely works. :)

    0 讨论(0)
  • 2021-02-06 16:29

    So, what I ended up doing is this. I'm not sure if it's the best way to go about it, but it seems to give me the results I'm looking for.

    class Order
      def dynamic_attributes
        self.attributes.delete_if { |attribute| 
          self.fields.keys.member? attribute 
        }
      end
    end
    

    Attributes appears to be a list of the actual attributes on the object, while fields appears to be a hash of the fields that were predefined. Couldn't exactly find that in the documentation, but I'm going with it for now unless someone else knows of a better way!

    0 讨论(0)
  • 2021-02-06 16:33

    try .methods or .instance_variables

    0 讨论(0)
  • 2021-02-06 16:37

    this will give you only the dynamic field names for a given record x:

    dynamic_attribute_names = x.attributes.keys - x.fields.keys
    

    if you use additional Mongoid features, you need to subtract the fields associated with those features: e.g. for Mongoid::Versioning :

    dynamic_attribute_names = (x.attributes.keys - x.fields.keys) - ['versions']
    

    To get the key/value pairs for only the dynamic attributes:

    make sure to clone the result of attributes(), otherwise you modify x !!

    attr_hash = x.attributes.clone  #### make sure to clone this, otherwise you modify x !!
    dyn_attr_hash = attr_hash.delete_if{|k,v| ! dynamic_attribute_names.include?(k)}
    

    or in one line:

    x.attributes.clone.delete_if{|k,v| ! dynamic_attribute_names.include?(k)}
    
    0 讨论(0)
  • 2021-02-06 16:38

    Not sure if I liked the clone approach, so I wrote one too. From this you could easily build a hash of the content too. This merely outputs it all the dynamic fields (flat structure)

     (d.attributes.keys - d.fields.keys).each {|a| puts "#{a} = #{d[a]}"};
    
    0 讨论(0)
提交回复
热议问题