I'm working on my first Rails app. It needs to store some information about a single advertisement that will appear on every page. The admin just needs to be able to set the URL, Title, and an image. Obviously, I only need one instance of this ad object.
I created a model that inherits from ActiveRecod::Base, but that seems like the wrong thing to do, since it is configured to save multiple ads in a database table.
What's the best way for me to do this? What should the model and controller look like?
Thanks in advance,
Avi
A better way would be adding a validation that checks if one record entry already exists.
Inside your model:
validate :check_record, on: :create #please not that validate in this case is singular
def check_record
if Ad.all.count === 1
errors[:base] << "You can only have one active advertisement"
end
end
Well, if you are going to store the information (URL, Title, image) in the database, I think having it inheriting from AR is the right thing to do. If they are going to do this through any sort of front-end, I think this is your best option.
Why not allow for multiple advertisements, but only one of them can be published at a time? That way, you can also have a history of the advertisements—I don't know how important that part is, but it might prove interesting down the line.
I would suggest that you continue using ActiveRecord but that you add a boolean attribute that determines which of the many advertisement records is the active one. I have called this field active within the following example.
You can then validate within the model that a maximum of one record is active. The validation of the active attribute should succeed under any of the following conditions
The active attribute is set to false
There are 0 records with an active value of true.
The current record already has the attribute set to true in the database.
The following class should meet your needs
class Ad < ActiveRecord::Base
named_scope :has_active, :conditions => {:active => true}
def validate
errors.add_to_base "You can only have one active advertisement"
unless self.active_flag_valid?
end
def active_flag_valid?
self.active == false ||
Ad.has_active.size == 0 ||
( Ad.has_active.size == 1 && !self.active_changed?)
end
end
I agree. If you're completely sure it's static, then it doesn't even need to be stored in the database. If it does change, Rails will give you created_at and updated_at pretty much for free, so getting the most recently created / updated is one simple way to approach displaying what's current.
Related
My Rails app has many models that form a hierarchy. For example: Retailer > Department > Product Category > Product > Review.
A business requirement is that high-authority users can "share" any individual element in the hierarchy with a new or existing "normal" user. Without having an object shared with them, normal users have no rights to see (or do anything else) with any object in any level of the hierarchy.
The sharing process includes a choice of whether the share grants permission to read-only, read-update, or full CRUD on the target object.
Sharing any object grants R/O, R/W or CRUD permission to that object and all lower level objects in the hierarchy, and R/O permission to all of the direct ancestors of the object. The object collection grows organically, so the permission system works by just logging the user_id, the object_id of the share, and the nature of the share (R/O, CRUD, etc). As the population of objects in this hierarchy grows all the time, it is impractical to create an explicit permission record in the DB for every user/object combination.
Instead, at the start of the user request cycle, ApplicationController gathers all the permission records (user X has CRUD permission to Department #5) and holds them in a hash in memory. A Permissions model knows how to evaluate the hash when any object is passed to it - Permission.allow?(:show, Department#5) would return true or false depending on the content of the user's permission hash.
Let's take, for example, the Department model:
# app/models/department.rb
class Department < ActiveRecord::Base
after_initialize :check_permission
private
def check_permission
# some code that returns true or false
end
end
When the check_permission method returns true, I want Department.first to bring back the first record in the database as normal, BUT, if check_permission returns false, I want to return nil.
Right now, I have a solution whereby default scopes trigger a permissions check, but this is causing 2X the number of queries, and for classes with a lot of objects, memory problems and time/performance issues are sure to be on the horizon.
My goal is to use after_initialize callbacks to pre-permission the objects.
It would appear however that after_initialize is unable to block the original object from being returned. It does allow me to reset the values of the attributes of the object, but not to dispense with it.
Anybody know how to achieve this?
EDIT:
Many thanks for all of the answers and comments offered so far; hopefully this extended version of the question clarifies things.
Basically you need to check for access rights (or permissions) before returning a database query result. And you are trying to integrate this logic into your models.
It is possible, but not with the design you described in your question. It is not clean to implement this directly in ActiveRecord adapter methods (such as first, all, last etc...). You need to rethink your design.
(skip to point 'D' if this is too much reading)
You have several choices, which all depend on the way your permissions are defined. Let's look at few cases:
A. A user have a list of departments he owns and only him can access them
You can simply implement this as a has_many/belongs_to association with Active Record Associations
B. Users and Departments are independent (in other words: no ownership such as described in the previous case) and permission can be set individually for each users and each departments.
Simply again, you can implement a has_and_belongs_to_many association with Active Record Associations. You will need to create web logic so the administrator of your application can add/edit/remove access rights.
C. More complex case: the existing authorization libraries
Most people will turn to authorization solutions such as cancan, pundit or other
D. When those authorization libraries are oversized for your needs (actually, my case in most of my projects), I found that implementing authorization through rails scoping answers all my needs.
Let's see it through a simple example. I want administrators to be able to access the whole database records ; and regular users to access only departments with status = open and only during operation hours (say 8am-6pm). I write a scope that implement my permission logic
# Class definition
class Department
scope :accessible_by -> (user) do
# admin user have all access, always
if user.is_admin?
all
# Regular user can access only 'open' departments, and only
# if their request is done between 8am and 6pm
elsif Time.now.hour >= 8 and Time.now.hour <= 18
where status: 'open'
# Fallback to return ActiveRecord empty result set
else
none
end
end
end
# Fetching without association
Department.accessible_by(current_user)
# Fetching through association
Building.find(5).departments.accessible_by(current_user)
Defining a scope obliges us to use it everywhere in our code. You can think of the risk to "forget" going through the scope and accessing directly the model (i.e writing Department.all instead of Department.accessible_by(current_user)). So that's why you must solidly test your permissions in your specs (at the controller or features level).
Note In this example we do not return nil when the permission fails (as you mentioned in your question), but an empty result set instead. It is generally better so you keep the ActiveRecord method chaining capability. But you could also raise an exception and rescue it from your controller then redirect to a 'not authorized' page for example.
That is not what the after_initialize callback is used for. Instead, you could just define a method that does the same thing. For example, put this in your Department model and it should achieve the results you are looking for:
def self.get_first
check_permission ? first : nil
end
UPDATE
I'm not exactly sure how safe something like this would be, but you could just override the all method as the other query methods are based off of it.
class Department < ActiveRecord::Base
def self.all
check_permission ? super : super.none
end
private
def self.check_permission
# some code that returns true or false
end
end
You are probably better off using some authorization framework though.
UPDATE 2
Thinking about this a little more, I strongly recommend using a different approach. You really shouldn't be overriding methods like all as there will surely be unintended side effects.
A practical alternative would be to create a has_and_belongs_to_many relationship between Department and User. Here is how you would set it up:
user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :departments
...
end
department.rb
class Department < ActiveRecord::Base
has_and_belongs_to_many :users
...
end
Then run these commands in your terminal:
rails g migration CreateJoinTableDepartmentsUsers departments users
rake db:migrate
Now you can add users to a department with #department.users << #user, or departments to a user with #user.departments << #department. This should achieve the functionality that you are looking for.
#user.departments will return only departments for that user, #user.departments.first will return the first department for that user or nil if it doesn't have any, and #user.departments.find(1) will return the corresponding department only if it belongs to the user or throw an exception otherwise.
You can use before_create callback to stop record creation if check permission is false. Just return false in check_permission filter and record will be not created.
class Department < ActiveRecord::Base
before_create :check_permission
private
def check_permission
# return false if permission is not allowed
end
end
So a situation came up at work and I wanted to discuss it here because we could not get to an agreement between us:
We have two models, Order and Passport, which are related in a way that an Order has_one passport and a passport has_many orders. Whenever an order is completed, its associated passport must be 'locked', that is, turned into read-only (that information was already used to clear customs, so it can't be changed afterwards). We want to enforce that rule in the Passport model and we've thought of the following options:
Creating a validation. CONS: There will be records yielding valid? => false when technically the record is fine (although it can't be saved). For example, if other records have a validates_associated :passport on them, that could be a problem.
Overriding the readonly? method. CONS: This will raise an exception when trying to update that record, although you would expect that calling a save method won't ever raise one.
Creating a before_save callback. This has two flavors: either raise an exception (which is pretty much like the readonly? option) or add an #error and return false to stop the callback chain. CONS: Adding validation errors from outside a proper validation can be considered a bad practice. Also, you might find yourself calling valid? and getting true and then call save and get false.
This situation made us think a lot about the relationship between validations and Rails. What exactly does it mean for a record to be valid?? Does it imply that the save will work?
I would like to listen to your opinions to learn about this scenario. Maybe the best approach is neither one of the three! Thanks!
What about marking this record as read-only by using readonly! instance method? See the API
You could do it in a constructor, like:
class Passport < ActiveRecord::Base
def initialize(*args)
super(*args)
readonly! if orders.count>0 # or similar
end
end
I think there is an extra alternative. What you describe dictates that the Passport model can have some different states. I would consider using a state machine to describe the relevant orders status for the passport.
eg:
open
pending
locked
other_update_actions ...
With that in mind, all relevant order actions will trigger an event to the passport model and its state.
If it is possible to integrate the update actions to certain events then you could handle the readonly part in a more elegant way (incompatible state transition).
As an extra check you can always keep an ugly validator as a last resort to prevent the model from being updated without the state machine.
you can check the aasm gem for this
I am using Rails 3 and want whenever user will be created, then separate Data Base should be created for newly created user.
e.g if I have 13 migration in my application, so 13 tables should be created for newly created user.
How I can achieve this functionality?
Also check out the audio/video that goes along with James slides here http://www.confreaks.com/videos/889-railsconf2012-ten-things-you-didn-t-know-rails-could-do. Move forward to around 15:30 for the section on one user per database.
This is a really bad idea to handle many DB in Rails!
You can add a field, say, user_id in each of your tables that are needed to be separated and then apply default_scope to their respective models (or make an abstract model with default_scope via self.abstract_class = true and inherit your "shareable" models from it).
Your default_scope, as you might guess, should look like:
default_scope lambda { where(user_id: current_user_id) }
How to get the current user, you may ask?
Models can't access session, so you can make the following "hack" in order your scope to work:
#in ApplicationController
before_filter do
user_id = session[:user_id]
ActiveRecord::Base.class.send :define_method, :current_user_id, lambda { user_id }
end
I guess you got the idea.
This is best post i follow and solve my problem
http://7fff.com/2010/12/02/activerecord-dropcreate-database-run-migrations-outside-of-rails/
I have a requirement that certain attribute changes to records are not reflected in the user interface until those changes are approved. Further, if a change is made to an approved record, the user will be presented with the record as it exists before approval.
My first try...
was to go to a versioning plugin such as paper_trail, acts_as_audited, etc. and add an approved attribute to their version model. Doing so would not only give me the ability to 'rollback' through versions of the record, but also SHOULD allow me to differentiate between whether a version has been approved or not.
I have been working down this train of thought for awhile now, and the problem I keep running into is on the user side. That is, how do I query for a collection of approved records? I could (and tried) writing some helper methods that get a collection of records, and then loop over them to find an "approved" version of the record. My primary gripe with this is how quickly the number of database hits can grow. My next attempt was to do something as follows:
Version.
where(:item_type => MyModel.name, :approved => true).
group(:item_type).collect do |v|
# like the 'reify' method of paper_trail
v.some_method_that_converts_the_version_to_a_record
end
So assuming that the some_method... call doesn't hit the database, we kind of end up with the data we're interested in. The main problem I ran into with this method is I can't use this "finder" as a scope. That is, I can't append additional scopes to this lookup to narrow my results further. For example, my records may also have a cool scope that only shows records where :cool => true. Ideally, I would want to look up my records as MyModel.approved.cool, but here I guess I would have to get my collection of approved models and then loop over them for cool ones would would result in the very least in having a bunch of records initialized in memory for no reason.
My next try...
involved creating a special type of "pending record" that basically help "potential" changes to a record. So on the user end you would lookup whatever you wanted as you normally would. Whenever a pending record is apply!(ed) it would simply makes those changes to the actual record, and alls well... Except about 30 minutes into it I realize that it all breaks down if an "admin" wishes to go back and contribute more to his change before approving it. I guess my only option would be either to:
Force the admin to approve all changes before making additional ones (that won't go over well... nor should it).
Try to read the changes out of the "pending record" model and apply them to the existing record without saving. Something about this idea just doesn't quite sound "right".
I would love someone's input on this issue. I have been wrestling with it for some time, and I just can't seem to find the way that feels right. I like to live by the "if its hard to get your head around it, you're probably doing it wrong" mantra.
And this is kicking my tail...
How about, create an association:
class MyModel < AR::Base
belongs_to :my_model
has_one :new_version, :class_name => MyModel
# ...
end
When an edit is made, you basically clone the existing object to a new one. Associate the existing object and the new one, and set a has_edits attribute on the existing object, the pending_approval attribute on the new one.
How you treat the objects once the admin approves it depends on whether you have other associations that depend on the id of the original model.
In any case, you can reduce your queries to:
objects_pending_edits = MyModel.where("has_edits = true").all
then with any given one, you can access the new edits with obj.new_version. If you're really wanting to reduce database traffic, eager-load that association.
I have a model called Contacts.
Contacts can have different status "bad, positive, wrong..."
These status may need to be changed over time, but across all contacts, they are the same options.
Should I model it this way:
Contacts.rb
belongs_to :status_contact
StatusContacts.rb
has_many :contacts
Then I manually populate the types of status in the table?
I then want to use Ajax to click a button corresponding to a value to update the value for Contacts.
It looks like you're trying to ensure that the values for your status are always going to restricted to a set of possible answers of your choosing. If that's all you're trying to do, there's no special need for a separate table. You can use the magic of ActiveRecord validations instead here.
First, create a string database column for Contact called :status.
Then you can use a validation to ensure that the values are limited to the ones you want. In Rails 3, you can do it like this:
validate :status, :inclusion => { :in => %w( bad positive wrong ) }
(If you're using Rails 2, use #validates_inclusion_of instead.)
In ActiveRecord, validations check that the object's values are valid before saving; it refuses to persist the object into the database until all validations pass.
Your naming strikes me as a little weird—ContactStatus sounds a little nicer to me—but I see this as being the general idea to achieve what you want.
No clear answer yet --- I think I need to use the table because it would allow the users to add and modify the types of status used across their application.