Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List dynamic attributes in a Mongoid Model

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 iterate over all of them.

So, for a concrete example:

class Order
  include Mongoid::Document

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

And then I do the following:

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

And later I want to come back and be able to get a list/array of all the dynamic attributes (in this case, "internal_id" is it).

I'm still digging, but I'd love to hear if anyone else has solved this already.

like image 629
Christopher WJ Rueber Avatar asked Dec 20 '22 19:12

Christopher WJ Rueber


2 Answers

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
like image 88
skiz Avatar answered Dec 23 '22 09:12

skiz


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)}
like image 31
Tilo Avatar answered Dec 23 '22 09:12

Tilo