I'm trying to write an example app using Ruby on Rails and the Mongoid Mapper.
For some kind of Testing I want to write 1000 Testusers into MongoDB...
With the code bolow Mongoid is not able to write unique uid's. In my ruby console i got the right number for the counter but not for the uid.
Does anybody know what I forgot?
class User
include Mongoid::Document
include Mongoid::Timestamps
def self.create_users
(1..1000).each do |f|
user = User.new(uid: f.to_s, first_name: "first_name", last_name: "last_name", e_mail: "e_mail")
user.save!
puts f
puts user.uid
end
end
field :uid, :type => String
field :first_name, :type => String
field :last_name, :type => String
field :e_mail, :type => String
field :messages, :type => String
attr_accessible :first_name, :last_name, :e_mail
validates_presence_of :uid, :first_name, :last_name, :e_mail
validates_uniqueness_of :uid
has_many :messages
end
You don't have to provide the field uid in your models. MongoId add the id field for you and manages the value during the create operation.
Simply remove field :uid, :type => String from model
If you want to use your own ids you can change the name of the uid field to _id and it should work just fine. However, the default generated mongo _id will make it easier to scale and using it removes one of the more difficult aspects of sharding if you ever need that feature.
If you want to use the ones that are generated by default, they are included automatically unless overridden explicitly (behavior which you have seen) so just remove your custom field and you should be all set.
You can read more about ObjectIds here.
Related
I would like to use a model in Rails but not store it in DB. For example let's say I have a RSS reader. I would download some RSS data from other site and then create objects with specific model and show them to use. I don't want to store those objects in databse though. How can I do it?
I think your problem might easily be solved by just creating a class, alternatively you can use ActiveModel, it allows for the same behaviour without storing it in the db.
class RssReader
#include any behaviour you like
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
end
There is a very nice railscast on this at:
http://railscasts.com/episodes/219-active-model
You can also check this out(Rails 4)
http://blog.remarkablelabs.com/2012/12/activemodel-model-rails-4-countdown-to-2013
You're looking for tableless models, there are plenty of questions on SO about this:
Rails model without database
Rails Model without a table
and a handy railscast!
http://railscasts.com/episodes/193-tableless-model
In rails 2.3 You can do this by this way:
class OrderSession < ActiveRecord::Base
def self.columns() #columns ||= []; end
column :orderable_id, :integer
column :orderable_type, :string
column :subscription, :boolean
column :interval, :integer
column :quantity, :float
column :number, :integer
column :only_with_main_cart, :boolean
column :start_on, :date
attr_accessor :choice_item
attr_accessor :interval_choice_item_1
attr_accessor :interval_choice_item_2
validates_presence_of :orderable_id
validates_presence_of :orderable_type
validates_presence_of :interval
validates_numericality_of :quantity, :greater_than => 0
validates_inclusion_of :subscription, :in => [true, false]
validates_inclusion_of :only_with_main_cart, :in => [true, false]
end
I am using this for storing cart information before user confirmation
I am testing this from rails console:
Credential.last.token => nil
Credential.last.update_attribute :token, '123' => true
Credential.last.token => nil
Here is my class:
class Credential
include Mongoid::Document
include Mongoid::Timestamps
field :_id, type: String
field :user_id, type: Integer
field :code, type: String
field :provider, type: String
field :token, type: String
end
What am I doing wrong?
If you have identity map enabled you'll need to wrap that in
Mongoid.unit_of_work { Credential.last.token }
Mongoid caches the queries. It is not a problem for web requests, but in the console you won't see the change unless you do it in the unit of work block, or restart the console (not just a reload)
I had to put
attr_accessor :token, ...
I have 2 model:
class User
include Mongoid::Document
field :email, :type => String, :null => false, :default => ""
.
.
end
class Admin
include Mongoid::Document
field :email, :type => String, :null => false, :default => ""
.
.
end
I want with a mongoid query find all users have a equal email in Admin model, something like:
User.where(:email => {exist_admin_class?})
This is possible? Or I have make a relationship between two model with a has_one User and belongs_to Admin
What is the best way to do this?
Thank you very much!
Indeed, MongoDB doesnt support cross collection queries. But it isnt necessary, especially not in this requirement. I would suggest using inheritance for that:
mongoid HowTo
Why: Just because admins are a special kind of users.
I am using Devise for authentication, so I've aliased a few columns in my legacy database to accommodate it as follows:
class User < ActiveRecord::Base
set_table_name 'my_legacy_user_table'
set_primary_key 'UserId'
alias_attribute :id, :UserId
alias_attribute :username, :LoginId
alias_attribute :encrypted_password, :PasswordSHA1Hash
alias_attribute :first_name, :Name
alias_attribute :last_name, :Surname
devise :database_authenticatable, :authentication_keys => [:username]
attr_accessible :username, :password, :password_confirmation
def password_salt=(password_salt)
end
def password_salt
end
def password_digest(password)
self.class.encryptor_class.digest(password)
end
end
When I post to my /users/sign_in form, I get the following exception:
Mysql2::Error: Unknown column 'my_legacy_user_table.username' in 'where clause': SELECT `kms_User`.* FROM `my_legacy_user_table` WHERE (`my_legacy_user_table`.`username` = 'mrichman') LIMIT 1
I suppose I was under the assumption that alias_attribute would instruct ActiveRecord to use the real column name (UserId) and not the alias (username). What am I doing wrong?
Arel (the one that makes the SQL queries) still is not aware of ActiveRecord's aliases (up to 3.0.3). You should make sure that the query is made using the original name, LoginId, in this case.
If you enter the console and make a User.where(:username => "root") you see that it generates an error, although User.username works well.
For now just replace the username occurrences on sinup form until the upstream starts to support it.
EDIT: By the way, the recommended way of doing that is make a view! Don't forget that!
http://www.slideshare.net/napcs/rails-and-legacy-databases-railsconf-2009
I've been doing a spike on Rails 3 and Mongoid and with the pleasant memories of the auto scaffolding in Grails I started to look for a DRY view for ruby when I found:
http://github.com/codez/dry_crud
I created a simple class
class Capture
include Mongoid::Document
field :species, :type => String
field :captured_by, :type => String
field :weight, :type => Integer
field :length, :type => Integer
def label
"#{name} #{title}"
end
def self.column_names
['species', 'captured_by', 'weight', 'length']
end
end
But since dry_crud depends on self.column_names and the class above doesn't inherit from ActiveRecord::Base I have to create my own implementation for column_names like the one above. I would like to know if it possible to create a default implementation returning all of the fields above, instead of the hard coded list?
Why would you go through the trouble of doing all that when there's an in-built method?
For Mongoid:
Model.attribute_names
# => ["_id", "created_at", "updated_at", "species", "captured_by", "weight", "length"]
Short of injecting a new method in Mongoid::Document you can do this in your model.
self.fields.collect { |field| field[0] }
Update : Uhm better yet, if you fell adventurous.
In the model folder make a new file and name it model.rb
class Model
include Mongoid::Document
def self.column_names
self.fields.collect { |field| field[0] }
end
end
Now your model can inherit from that class instead of include Mongoid::Document. capture.rb will look like this
class Capture < Model
field :species, :type => String
field :captured_by, :type => String
field :weight, :type => Integer
field :length, :type => Integer
def label
"#{name} #{title}"
end
end
Now you can use this natively for any model.
Capture.column_names