Ruby Mongoid::Errors::InvalidField - ruby

class MyModule::MyModel
include Mongoid::Document
field :field1, :type=>Integer
...
field :fieldn, :type=>Integer
field :deleted, :type=>Boolean
store_in session: 'mydb', collection: 'mycollection'
end
These code threw Mongoid::Errors::InvalidField when came to :deleted definition. If I remove this line, it works well.
/var/lib/gems/2.1.0/gems/mongoid-4.0.0/lib/mongoid/fields/validators/macro.rb:56:in `block in validate_name': (Mongoid::Errors::InvalidField)`
As http://www.rubydoc.info/github/mongoid/mongoid/Mongoid/Errors/InvalidField says,
This error is raised when trying to create a field that conflicts with
an already defined method.
How can I use this conflicted name?

When I try to add a deleted field, Mongoid 4.0.2 says:
Problem:
Defining a field named 'deleted?' is not allowed.
Summary:
Defining this field would override the method 'deleted?', which would cause issues with expectations around the original method and cause extremely hard to debug issues. The original method was defined in:
...
When you say:
field :f
Mongoid creates three methods for that field: f (getter), f= (setter), and f? (is f truthy AFAIK). The last one is causing your problem because Mongoid has its own deleted? method.
Your best bet would be to use a different name for that field, field :is_deleted perhaps.
If you can't do that (i.e. you're attaching Mongoid to a predefined collection), then you could use dynamic attributes:
class MyModule::MyModel
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :field1, :type=>Integer
...
field :fieldn, :type=>Integer
# Don't define the field here
store_in session: 'mydb', collection: 'mycollection'
end
and then you'd access it use Mongoid's [] and []= methods:
d = MyModule::MyModel.new
d[:deleted] = true
d = MyModule::MyModel.find(id)
puts d[:deleted]
puts d.attributes['deleted']
You could also add your own is_deleted and is_deleted= methods that would use [] and []= to update the underlying attribute.

Related

Correct way to define virtual attributes on a Model for the keys in a JSON column in rails

In my rails model I have a JSON column which stores some meta information.
This is to be entered bu the user from a form.
Since the keys of the JSON column are not attributes of the model I cannot use them directly in form_for instead I need to define a virtual attribute.
Since this number of virtual attributes could grow to be arbitrarily lengthy I would like to use meta programming to define the attributes.
I did try the answer in this question however when I use the constant in my model I get an error saying that the constant is undefined. So I added the symbols for the keys in an array directly and iterate over them in the module. When I do this I get an error that says stack level too deep.
Please can someone help me out here?
If you are using PostgreSQL specific columns like hstore or json simply use store_accessor instead to generate the accessor methods. Be aware that these columns use a string keyed hash and do not allow access using a symbol.
class Model < ActiveRecord::Base
store_accessor :my_json_column, [ :key_1, :key_2, key_3 ]
end
What it doing under the hood? It has define write\read helper methods:
def store_accessor(store_attribute, *keys)
keys = keys.flatten
_store_accessors_module.module_eval do
keys.each do |key|
define_method("#{key}=") do |value|
write_store_attribute(store_attribute, key, value)
end
define_method(key) do
read_store_attribute(store_attribute, key)
end
end
end
# .....
store
I figured it out. I return the attribute as a key of the JSON column and it works fine now.
# lib/virtuals.rb
module Virtuals
%W(key_1 key_2 key_3).each do |attr|
define_method(attr) do
self.my_json_column[attr]
end
define_method("#{attr}=") do |val|
self.my_json_column[attr] = val
end
end
end
In my Model i just need to include that above module and it works fine in the form_for and updates correctly as well.

Querying mongoid for value in attribute array

I need to search within Mongoid objects that have array attributes. Here are the relevant objects:
class Author
include Mongoid::Document
field :name, type: String
class Book
include Mongoid::Document
field :name, type: String
field :authors, type: Array
I can see that at least one book has a given author:
Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
BSON::ObjectId('5363c73a4d61635257835e00'),
BSON::ObjectId('5363c73a4d61635257c75e00'),
BSON::ObjectId('5363c73b4d616352574a5f00')]
But I'm unable to find books that have that author.
Book.where(authors: '5363c73a4d61635257805e00').first
=> nil
I've tried the solution listed here: https://groups.google.com/forum/#!topic/mongoid/csNOcugYH0U but it didn't work for me:
Book.any_in(:author => ["5363c73b4d616352574a5f00"]).first
=> nil
I'm not sure what I'm doing wrong. Any ideas? I'd prefer to use Mongoid Origin commands.
This output:
Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
BSON::ObjectId('5363c73a4d61635257835e00'),
BSON::ObjectId('5363c73a4d61635257c75e00'),
BSON::ObjectId('5363c73b4d616352574a5f00')]
tells us that authors contains BSON::ObjectIds. ObjectIds are often presented as Strings and sometimes you can use a String instead of a full blown ObjectId (such as with Model.find) but they're still not Strings. You are searching the array for a String:
Book.where(authors: '5363c73a4d61635257805e00')
but '5363c73a4d61635257805e00' and ObjectId('5363c73a4d61635257805e00') are not the same thing inside MongoDB. You need to search for the right thing:
Book.where(authors: BSON::ObjectId('5363c73a4d61635257805e00'))
You might want to monkey patch a to_bson_id method into various places. Something like this:
class String
def to_bson_id
BSON::ObjectId.from_string(self)
end
end
module Mongoid
module Document
def to_bson_id
id
end
end
end
module BSON
class ObjectId
def to_bson_id
self
end
end
end
class NilClass
def to_bson_id
self
end
end
Should do the trick. Then you can say things like:
Book.where(authors: '5363c73a4d61635257805e00'.to_bson_id)
Book.where(authors: some_string_or_object_id.to_bson_id)
and The Right Thing happens.
You might want to rename authors to author_ids to make its nature a little clearer.

field vs method ruby on rails

I have this class:
class User
include Mongoid::Document
field :revenues, :type => Integer, :default => nil
attr_accessible :revenues
#now method
def revenues
return 1
end
end
Why in console I get 1 instead nil?
1.9.3-p125 :002 > u.revenues
=> 1
Which has priority, the method or the field? How can I created a method with the same features that a field?
The field macro is defined in Mongoid::Document. It is neither a syntatic feature from Ruby nor from Rails.
What's happening with your code is the following:
The field function creates for you some methods, one of them is called revenues.
When you create another method called revenues, you are in effect overwriting the previously defined method, therefore making it useless.
Short answer: I don't understand a zip about Mongoid, but chances are that your field still exists even after you defined oce again a method named revenues. The only drawback is that you cannot access it by calling myUser.revenues anymore.
Try to make a test: access your field with the notation some_user[:revenues] and see what happen :)
Best regards

Ruby Datamapper model wrapped around a module prefix module name in the table name in sql query

I have a datamapper model as:
module abc
class Post
include DataMapper::Resource
property :title, String
property :body, Text
end
end
Note, my class is wrapped around module abc
In other file
I have:
abc::Post.all
This should make call to query:
select * from posts
But it calls
select * from abc_posts
Table name get a prefix added abc_ . How can I remove it. I don't keep model around the module, then it works as expected. But my codebase needs model to be inside the module.
I found the solution at http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/NamingConventions/Resource/UnderscoredAndPluralizedWithoutModule
repository(:myreponame).adapter.resource_naming_convention = DataMapper::NamingConventions::Resource::UnderscoredAndPluralizedWithoutModule
change :myreponame
Also at http://datamapper.rubyforge.org/dm-core/DataMapper/NamingConventions.html
You can also set your storage name(s) explicitly:
http://datamapper.org/docs/legacy.html

Finding all documents in a collection with Mongoid

I have been fiddling with Mongo, but can't get this simple example to work. I'm simply trying to retrieve all documents in a collection:
require 'mongoid'
# configuration ...
class Category
include Mongoid::Document
field :name, type: String
end
Category.each do |test|
puts test.inspect
end
I get the error: undefined method 'each' for Category:Class (NoMethodError).
Connection to the database is well established, and a collection named categories contains a few documents.
Category indeed doesn't have a method each because it's a model class, not a collection. It has, however, several methods that do return collection-like objects. One of them is all. So the code should look like this:
Category.all.each do |test|
puts test.inspect
end

Resources