Rails 3 using MongoDB via mongoid adapter - is there any way to share attribute specifications without using Single-Table Inheritance?
Asked Answered
B

3

3

Probably a confusing title, but not sure how else to put it. Example should make it clearer. I have many different models that share many of the same attributes. So in each model I have to specify those same attributes and THEN the attributes that are specific to that particular model.

Is there any way I can create some class that lists these basic attributes and then inherit from that class without using Single-Table Inheritance? Because if I put all the shared attributes and Mongoid includes into a single model and inherit from that base model in the other models, then STI is enforced and all my models are stored in a single mongodb collection, differentiated by a "_type" field.

This is what I have:

class Model_1
  include Mongoid::Document

  field :uuid, :type => String
  field :process_date, :type => String
  ...
end

class Model_2
  include Mongoid::Document

  field :uuid, :type => String
  field :process_date, :type => String
  ...
end

But this is the functionality I'm after:

class Base_model
  field :uuid, :type => String
  field :process_date, :type => String
end

class Model_1 < Base_model
  # To ensure STI is not enforced
  include Mongoid::Document

  # Attribute list inherited from Base_model
end

The issue is that if you don't have the "include Mongoid::Document" in the Base_model, then that base model doesn't know about the "field ..." functionality. But if you do put the mongoid include in the base model and inherit from it, STI is enforced.

I can't do STI for this particular situation but it's a coding nightmare to have multiple models, all with the same attributes list specified over and over (there are a growing number of models and each share about 15-20 attributes, so anytime I have to change a model name it's a lot of effort to change it everywhere...).

Boaster answered 25/4, 2012 at 14:4 Comment(0)
E
4

You can define the common attributes in a module and include that.

require 'mongoid'

module DefaultAttrs

  def self.included(klass)
    klass.instance_eval do
      field :uuid, :type => String
    end
  end

end

class Foo
  include Mongoid::Document
  include DefaultAttrs

  field :a, :type => String
end

class Bar
  include Mongoid::Document
  include DefaultAttrs

  field :b, :type => String
end
Easley answered 25/4, 2012 at 19:23 Comment(2)
That's pretty cool; it's exactly what I was looking for. Is a module treated much like a model? Is it standard to put modules in the app/models folder or would it generally go in the /lib folder? Never really had a need to use modules or do some metaprogramming like that, but it is the exact answer I was looking for.Boaster
Modules are just containers. They are like classes but can't be instantiated and so they are frequently used as mix-ins in the Ruby programming language. The lib folder is a fine place for it (that or initializers).Jenna
D
0

I had the exact same question and wanted to go for the mixin approach initially. However, after talking to some experts it turns out that using mongoids single table inheritance (one collection for all child elements) may be the way to go, depending on your use case. Please see my post here: single collection vs. separate collections for inherited objects

Danuloff answered 24/5, 2012 at 14:32 Comment(0)
A
0

You can bundle them into a new module, i.e.

module Mongoid
  module Sunshine
    extend ActiveSupport::Concern

    included do
      include Mongoid::Document
      include Mongoid::Timestamps
      include Mongoid::TouchParentsRecursively
      include Mongoid::Paranoia
      include Mongoid::UUIDGenerator
    end
  end
end


class School
  include Mongoid::Sunshine
end
Applicable answered 1/11, 2013 at 3:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.