19

I'm building backend system, as written in Iain Hecker's tutorial: http://iain.nl/backends-in-rails-3-1 and I try to adapt it to MongoDB with Mongoid.

So when I need to write in backend/resourse_helper.rb

module Backend::ResourceHelper

  def attributes
    resource_class.attribute_names - %w(id created_at updated_at)
  end

end

I get the following error:

undefined method `attribute_names' for Backend::User:Class

(I rooted backend to "backend/users#index"). Backend::User inherits from User:

class User
  include Mongoid::Document

  devise_for :users

  field :name
  field :address
end

I just need a list of fields for that User:Class, as I guess (i.e. ["email", "name", "address", ...]), but I broke my head trying to find how.

Yuri Sidorov
  • 329
  • 1
  • 2
  • 16

4 Answers4

37

Mongoid already provides you the attributes for an object:

Model.new.attributes

To get the names for these attributes:

Model.fields.keys
Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • Based on the context of the example given, as well as the reported error message, I would assume that Yuri is working with a class here, not an instance. if that's true, requiring him to instantiate the class seems unnecessary. – bloudermilk Aug 10 '11 at 01:43
  • @Bloudermilk: Right you are. I have updated this answer to now use `Model.fields.keys`, as that will return the correct fields. – Ryan Bigg Aug 10 '11 at 03:25
  • Thank you, Ryan! Your solution worked for me. I removed Backend::User and wrote `a = []; resource_class.all.collect { |r| r.fields.keys.each { |k| a << k } }; a.uniq - %w(_id _type created_at updated_at encrypted_password)`. Don't you know how can I refactor this string? I'm little new to ruby) – Yuri Sidorov Aug 10 '11 at 08:40
  • 1
    @Yuri: First of all I wouldn't call the variable `a`. Try it give it a more descriptive name. `fields = resource_class.all.collect { |r| r.fields.keys } - %w(_id _type created_at updated_at encrypted_password)` would be how I would write this. – Ryan Bigg Aug 10 '11 at 22:43
  • @Ryan Bigg: This answer was a great help for me. I would like to know if we could find the fields data type along with find its name. – Abhay Kumar May 21 '12 at 10:52
  • Despite all the good feedback, i don't think that creating an instance just to get the field list is a good idea. This works a little bit better: `MongoidModel.fields.keys` – Elad Meidar Mar 23 '13 at 18:26
  • @RyanBigg Did you know how to get all field names + dynamic fields of the collection? – Mindbreaker Jun 24 '13 at 19:20
15

Use the built-in method:

Model.attribute_names
# => ["_id", "created_at", "updated_at", "name", "address"]
3

One thing to take note of is that Model.fields.keys will only list the field keys that are defined in the Model class. If you use dynamic fields they will not be shown. Model.attributes.keys will also include the attribute keys for any dynamic fields you have used.

RevDave
  • 124
  • 3
2

You're on the right track with attribute_names. I think you just need to make sure you're including your module in the proper place. For instance, if you had the same module:

module Backend::ResourceHelper
  def attributes
    resource_class.attribute_names - %w(id created_at updated_at)
  end
end

Your class should look like so:

class User
  include Mongoid::Document
  extend Backend::ResourceHelper

  devise_for :users

  field :name
  field :address
end

Then calling User.attributes should return ["name", "address"]

bloudermilk
  • 17,820
  • 15
  • 68
  • 98
  • No, that doesn't work, it returns the same `undefined method `attributes' for User:Class`. – Yuri Sidorov Aug 10 '11 at 01:11
  • If you're *including* `Backend::ResourceHelper` then the `attributes` method wouldn't be coming from that. Including a module into a class will make that module's methods available on instances of the class, not the class itself. – Ryan Bigg Aug 10 '11 at 01:13
  • Thank you, Ryan. I should have written `extend` (updated). Also, my answer assumes Yuri is working with the class. Requiring instantiation of the class would be an unreasonable solution, IMO. – bloudermilk Aug 10 '11 at 01:37