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
Related
I am trying to go table-less with activerecord-tableless. The main reason to use it is for the associations. Here(in the github documentation) it tells you how to simply work with one model but doesn't say anything about associations.
For example: I have a User and Category model. Each User belongs to a Category.
This is my User model
class User < ActiveRecord::Base
has_no_table
belongs_to :category
column :id, :integer
column :name, :string
column :email, :string
column :password, :string
column :created_at, :string
column :updated_at, :string
column :category_id, :integer
validates :name, :email, :password, presence: true
validates_length_of :password, minimum: 8
end
Now, in the form, I have to get all the categories to select from like this
<%= f.collection_select(:category_id, Category.all, :id, :title) %>
From where, Category.all can retrieve its content. Do I have to override it?
Also, suppose I have a User object and I need to find it's Category object(if I have a category id), then will 'find' work?
If everything has to be overridden, then what is the use of activerecord-tableless.
I am not able to find any documentation or tutorials regarding this. Please help.
PS: I have tried ActiveModel as well but they do not support associations.
PPS: Rails 4.1.1 and Ruby 1.9.3 is being used.
I'm using active admin with ActiveRecord scopes. However, I'm having an issue with adding the scopes.
Running ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux] and
Rails 4.2.5.1
#app/model/accounts.rb
class Account < ActiveRecord::Base
searchkick
belongs_to :program
belongs_to :insurance
has_many :notes
scope :program_name, -> (program) {where(program_name: adult) }
validates :first_name, :last_name, :address, :phone, presence: true
validates :phone, format: { with: /\A\d{3} \d{3}-\d{4}\z/,
message: "must be in the format 123 456-7890" }
end
I want to be able to us this in app/admin/account.rb
#app/admin/account.rb
ActiveAdmin.register Account do
menu :priority => 2
permit_params :first_name, :last_name, :return_client, :program_id, :insurance_id, :address, :phone
index do
column :first_name
column :last_name
column :address
column :phone
column :created_at
column :return_client
column :program
column :insurance
actions
end
scope :all, :default => true
scope :adult, default: true do |accounts|
accounts.program_name('adult')
end
end
I tired using it with and without block. I want the total count of "programs" in that scope as an end result.
You can't have two default scopes and the scope :all is unnecessary so remove it.
You have this scope which looks fine
scope :program_name, -> (program) {where(program_name: adult) }
and you say that
I want to be able to us this in app/admin/account.rb
but you aren't actually using it. You are instead trying to use
scope :adult, default: true do |accounts|
accounts.program_name('adult')
end
So just add it
scope :program_name
But your question seems to be loaded with something else you're trying to do
I want the total count of "programs" in that scope as an end result.
And in that ^ sense, I think you may be misunderstanding how and what scopes are actually used for.
I have a model which has some information which is best stored as a serialized Hash on the model, as it is unimportant to most of the app and varies from instance to instance:
class Foo < AR::Base
attr_accessible :name, :fields
serialize :fields
end
I have realised that one of the common entries in fields actually is relevant to the app, and would be better placed as an attribute (layout).
Bearing in mind that I should not, ideally, refer to models in migrations, how can I write a migration to add the layout field, and initialise it with the value currently in the fields Hash?
class AddLayoutToCardTemplates < ActiveRecord::Migration
def change
add_column :card_templates, :layout, :string, default: 'normal'
# Initialise `layout` from `fields['layout']`... how? With raw SQL?
end
end
You should not refer to models in your app folder. This doesn't mean you cannot create local model. :)
class AddLayoutToCardTemplates < ActiveRecord::Migration
class Foo < AR::Base
attr_accessible :name, :fields
serialize :fields
end
def change
add_column :card_templates, :layout, :string, default: 'normal'
Foo.all.each do |f|
f.layout = f.fields.delete(:layout)
f.save
end
end
That way your migration can use ActiveRecord goodies and yet stays time-independent, as your real model in app folder is never loaded.
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
Suppose the following DB migration in Ruby:
create_table :question_votes do |t|
t.integer :user_id
t.integer :question_id
t.integer :vote
t.timestamps
end
Suppose further that I wish the rows in the DB contain unique (user_id, question_id) pairs. What is the right dust to put in the model to accomplish that?
validates_uniqueness_of :user_id, :question_id seems to simply make rows unique by user id, and unique by question id, instead of unique by the pair.
validates_uniqueness_of :user_id, :scope => [:question_id]
if you needed to include another column (or more), you can add that to the scope as well. Example:
validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column]
If using mysql, you can do it in the database using a unique index. It's something like:
add_index :question_votes, [:question_id, :user_id], :unique => true
This is going to raise an exception when you try to save a doubled-up combination of question_id/user_id, so you'll have to experiment and figure out which exception to catch and handle.
The best way is to use both, since rails isn't 100% reliable when uniqueness validation come thru.
You can use:
validates :user_id, uniqueness: { scope: :question_id }
and to be 100% on the safe side, add this validation on your db (MySQL ex)
add_index :question_votes, [:user_id, :question_id], unique: true
and then you can handle in your controller using:
rescue ActiveRecord::RecordNotUnique
So now you are 100% secure that you won't have a duplicated value :)
From RailsGuides. validates works too:
class QuestionVote < ActiveRecord::Base
validates :user_id, :uniqueness => { :scope => :question_id }
end
Except for writing your own validate method, the best you could do with validates_uniqueness_of is this:
validates_uniqueness_of :user_id, :scope => "question_id"
This will check that the user_id is unique within all rows with the same question_id as the record you are attempting to insert.
But that's not what you want.
I believe you're looking for the combination of :user_id and :question_id to be unique across the database.
In that case you need to do two things:
Write your own validate method.
Create a constraint in the database
because there's still a chance that
your app will process two records at
the same time.
When you are creating a new record, that doesn't work because the id of your parent model doesn't exist still at moment of validations.
This should to work for you.
class B < ActiveRecord::Base
has_many :ab
has_many :a, :through => :ab
end
class AB < ActiveRecord::Base
belongs_to :b
belongs_to :a
end
class A < ActiveRecord::Base
has_many :ab
has_many :b, :through => :ab
after_validation :validate_uniqueness_b
private
def validate_uniqueness_b
b_ids = ab.map(&:b_id)
unless b_ids.uniq.length.eql? b_ids.length
errors.add(:db, message: "no repeat b's")
end
end
end
In the above code I get all b_id of collection of parameters, then compare if the length between the unique values and obtained b_id are equals.
If are equals means that there are not repeat b_id.
Note: don't forget to add unique in your database's columns.