Список динамических атрибутов в монгоидной модели

Я просмотрел документацию и не могу найти конкретный способ сделать это. Я уже добавил некоторые динамические атрибуты в модель, и я хотел бы иметь возможность перебирать их все.

Итак, для конкретного примера:

class Order
  include Mongoid::Document

  field :status, type: String, default: "pending"
end

И тогда я делаю следующее:

Order.new(status: "processed", internal_id: "1111") 

А позже я хочу вернуться и иметь возможность получить список / массив всех динамических атрибутов (в данном случае это "internal_id").

Я все еще копаю, но я хотел бы услышать, решил ли кто-нибудь еще это уже.

6 ответов

Решение

Просто включите что-то подобное в вашу модель:

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

и вот спецификация, чтобы пойти с этим:

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

Это даст вам только имена динамических полей для данной записи x:

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

если вы используете дополнительные функции Mongoid, вам необходимо вычесть поля, связанные с этими функциями: например, для Mongoid::Versioning:

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

Чтобы получить пары ключ / значение только для динамических атрибутов:

Обязательно клонируйте результат attribute (), иначе вы измените 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)}

или в одну строку:

x.attributes.clone.delete_if{|k,v| ! dynamic_attribute_names.include?(k)}

Итак, что я в итоге сделал это. Я не уверен, что это лучший способ, но, похоже, он дает мне результаты, которые я ищу.

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

Атрибуты представляются списком фактических атрибутов объекта, а поля - хешем предопределенных полей. Я не могу точно найти это в документации, но я пока пойду с этим, если кто-то еще не знает лучшего способа!

Я не смог заставить работать ни одно из вышеперечисленных решений (так как мне не хотелось добавлять кусочки кода и кусочки кода к каждой модели, и по какой-то причине attributes Метод не существует на экземпляре модели, для меня.:/), поэтому я решил написать свой помощник, чтобы сделать это для меня. Обратите внимание, что этот метод включает как динамические, так и предопределенные поля.

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

Как только я добавил вышеупомянутое включение (include MongoidAttributeHelper) к каждой модели, с которой я хотел бы использовать этот метод, я могу получить список всех полей, используя User.get_all_attributes,

Конечно, это может быть не самый эффективный или элегантный метод, но он определенно работает.:)

Попробуйте.methods или.instance_variables

Не уверен, что мне понравился подход клонирования, поэтому я тоже написал один. Из этого вы также можете легко создать хэш контента. Это просто выводит все динамические поля (плоская структура)

 (d.attributes.keys - d.fields.keys).each {|a| puts "#{a} = #{d[a]}"};
Другие вопросы по тегам