Instance variable setting and getting - ruby-on-rails

There is Permission Model, which doesn't have related db table, and is used to authorization.
With before_filter authorize method creates new permission object, depending on user and optional test_id (another model).
The idea is that it checks if test belongs to user, it allows this user delete this test, if no, it cancels transaction.
So my initialize method:
class Permission
def initialize(user, *test_id)
#user = user
if !test_id.empty?
test_id.each do |t|
test = Test.find_by_id(t) #finding real test record
self.instance_variable_set(:"#test_#{t}", test) #should set #test_{id}
#an instance of this new permission refering to actual test record
end
end
allow :users, [:new,:create]
allow :root,[]
allow :session, [:create, :destroy]
allow :tests, [:new, :create]
if !test_id.empty?
for i in 0...test_id.length
t = self.instance_variable_get(:"#test_#{i}") #getting this test record.
if t.user_id == #user.id
allow :tests, [:edit,:lock,:unlock, :destroy ]
end
end
end
end
The problem is that what rails gets from instance_variable_get is nil. So or I set up instance #test_{id} wrong, or get it.
Thanks in advance

The instance variables you set in the top each are of the form #test_{record_id}. The instance variables you get in the for loop are of the form #test_{loop_index}. Just use the same loop as at the top.
test_id.each do |t|
trec = self.instance_variable_get(:"#test_#{t}") #getting this test record.
if trec.user_id == #user.id
allow :tests, [:edit,:lock,:unlock, :destroy ]
end
end

You are using the instance variables as a database with the primary key based on the variable name itself. Needless (I think?) to say, this is not a good practice. You can use instance_variable_set to set dynamic instance variable names, but in general, I feel this makes your classes unpredictable, as it is more difficult to keep track of how the behavior is implemented with respect to the data.
If you need to cache a number of objects in your instance, you can use a data structure like an array or a hash, and set that to its own instance variable.

Ok, guys, I've just found out, that I overcomplicated things hardly.
I really don't need to set or get instance variables in my case, so
if !test_id.empty?
test_id.each do |t|
test=Test.find_by_id(t)
if test.user_id==user.id
allow :tests, [:edit,:lock,:unlock, :destroy ]
end
end
end
works fine

Related

Rails: Canned/RESTful way for accessing data returned by a method of a model

In my app I have a User model which defines a history method that returns a list of Activity objects, showing the last N actions the user has carried out. The UserController#history method wires this with a view.
The code looks as follows:
class UserController < ApplicationController
def history
user = User.find(params[:id])
#history = user.history(20)
end
end
class User < ActiveRecord::Base
has_many :activities
def history(limit)
...
end
end
Naturally, I also added this line to my routes.rb file:
match '/user/:id/:action', :controller => 'user'
so now when I go to localhost:3000/user/8/history I see the history of user 8. Everything works fine.
Being a Rails NOOB I was wondering whether there is some canned solution for this situation which can simplify the code. I mean, if /user/8 is the RESTful way for accessing the page of User 8, is it possible to tell Rails that /user/8/history should show the data returned by invoking history() on User 8?
First of all the convention to name controllers is in the plural form unless it is only for a single resource, for example a session.
About the routes I believe you used the resources "helper" in your routes, what you can do is specify that the resource routes to users also has a member action to get the history like this
resources :users do
member do
get :history
end
end
I think there is no cleaner way to do this
You can check it here http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
As far as the rails standards are concerned, it is the correct way to show the history in your case. In rails controllers are suppose to be middle-ware of views and model, so defining an action history seems good to me.
And you can specify the routes in better way as:
resources :user do
get 'history', :on => :member #it will generate users/:id/history as url.
end

Checking if param is present

I'm relatively new to Rails and busy building an app with various access levels, for instance global_admin and company_admin. Now company_admin should only have access to a specific company and no others.
My routes:
resources :companies do
resources :groups do
resources :users
end
end
I created a helper to check access which contains the following:
if params[:company_id].present?
#company = Company.find(params[:company_id])
...
So if I call, for instance ^/companies/1/groups ^/companies/1/groups/1/users the query returns true and finds the company_id, but if I call ^/companies/1 or ^/companies/2 it returns false. Why is it not picking up the company_id if it is (or at least seems to be) present?
Thanks in advance!
When you're not accessing a nested resource, params[:company_id] becomes params[:id] instead.
Same thing with groups. If you access /companies/1/groups/1, params[:id] would give you the group's id, but if you access /companies/1/groups/1/users/1, params[:id] would give you the user's id instead, and the group's id would be in params[:group_id].

Use variable other than :id in rails 3 routes

I'm trying to get my rails 3 app to use a route that looks something like:
exampleapp.com/patients/123456
rather than
exampleapp.com/patients/1
where "123456" would be associated with the patient's medical record number (:mrn), which is already in the :patients table and is a unique integer. I want to use the :mrn in place of the usual :id. How would I go about this?
Sorry if this has already been asked - I couldn't find the terminology as to what I'm trying to do is called. Thanks!
You could do this,
class Patient < ActiveRecord::Base
self.primary_key = "mrn"
end
However, this will change a bunch of other things. The to_params will use mrn. The controller will still use params["id"], but the value will be the mrn field. The Patient.find method will work on the mrn field, but not the id field. (You can user Patient.find_by_mrn and Patient.find_by_id which will work on their specified fields.) Also, all foreign keys will be to the mrn value.
You can edit the mrn field, and you will still have an id field (unless you turn it off), however, editing could be a pain because all the foreign keys will have to be corrected.
Alternatively, if you just want to change the URL, then in your config/routes.rb file instead of
resources :patient
use
match "/patients/:mrn" => "patients#show"
match "/patients/:mrn" => "patients#update", :via => :put
You could just add this to your Patients model
def class Patient < ActiveRecord::Base
self.primary_key = "mrn"
end
You can get per-resource identifier customization by redefining the member_scope and nested_scope methods on the Resource instance.
resources :patients do
#scope[:scope_level_resource].tap do |u|
def u.member_scope
"#{path}/:mrn"
end
def u.nested_scope
"#{path}/:#{singular}_mrn"
end
end
end

Rails 3 & cancan: User should not be allowed to edit record but is able to?

I am trying to implement specific object (row) authorisation using cancan, I want it to work in a way that a user can only make a change(update/edit) to a Record if he/she has the role for that specific Record. after consulting the cancan docs I tried doing the following:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Record do |record|
user.can_edit(record)
end
end
end
class User
has_many :assignments
has_many :roles_as_editor, :through => :assignments, :class_name => "Role", :source => :role, :conditions => {:edit => true}
def rec_as_editor
self.roles_as_editor.collect{ |x| Record.where(:cp_secondary_id => x.record_id) }.flatten.uniq
end
def can_edit(rec)
rec_as_editor.include?(rec)
end
end
The can_edit method takes in a Record object and ensures that a User has the role necessary to make a change to it by returning true or false. this method is tested and works correctly so the problem seems to be with the CanCan code because when i try editing a Record that the user dosent hold the role for it still allows me to make changes to the Record, anyone know why this wont work?
If you require any further information please let me know through a comment.
Thank You
Are you authorizing the resource in the controller?
you should have load_and_authorize_resource in your controller
or
def edit
#critical_process = CriticalProcess.find(params[:id])
#this here is what you use
authorize! :edit, #critical_process
end
in your edit method inside the critical process controller.
I personally prefer to keep this logic completely separate from models so that I don't have to dig into model code to find authorization issues. In other words, user.can_edit checks for authorization which is what the ability file is supposed to be in charge of. Shouldn't matter though... in this case I think you might have a problem inside the can_edit method. I have used code that looks nearly identical to yours without problems many times like this:
can :manage, Record do |record|
user.has_role?(:record_manager)
end
I suggest including your code for can_edit or use the debugger to see what value gets returned from can_edit.
I think the problem comes from the way you query for the records that are supposed to have the user as an editor.
I copy/pasted your code and built the other associations from scratch.
And testing it in the console it works as expected when I use it:
>> u = User.last
>> a = Ability.new(u)
>> a.can :edit, Role.last
false
The only thing I changed is the query for the records: it seemed to look for a record that owns the role (your Role has a record_id) but then looks for the same key under cp_secondary_id.
I think something is wrong in your query, but what depends on your schema and associations:
roles_as_editor.collect{ |x| Record.where(:cp_secondary_id => x.record_id) }.flatten.uniq
as I understood your code we are traversing associations like this:
User=>Assignment<=Role(boolean edit flag)<=Record

Determine if count of related model > 0

I have a model called Stem. I need a 'thumbs up' feature, so I have created a second model called Thumb, which consists of stem_id and user_id.
I'm also using the restful authentication plugin for user credentials.
I have the 'thumbs up' button working, which adds a row to the thumbs table fine, but I'd like to be able to check if the currently logged in user has already given a thumbs up to this particular stem.
I tried adding this to the Stem model:
def thumbed
Thumb.count_by_sql ["SELECT COUNT(*) FROM thumbs WHERE user_id = ? AND stem_id = ?", current_user.id, self.id ]
end
The problem here is that the stem model has no access to the current_user variable the the controllers have.
Is there a way I can get access to this property, or alternatively, is there another way I could go about checking this? I was hoping to get this as a property in the model because the stems are passed over to a Flex app using RubyAMF.
Thanks!
Your controller knows the current_user, and your Stem model probably shouldn't. You can, however clean up your code and avoid hard-wiring SQL with a named_scope and pass the user into that.
#thumb.rb
named_scope :for_user_id, lambda {|id| {:conditions => {:user_id => id}}}
#stem.rb
def thumbed_by_user(user)
self.thumbs.for_user_id(user.id).count > 0
end
# some controller
stem = some_method_that_gets_a_stem
is_already_thumbed = stem.thumbed_by_user(current_user)
Can you pass the current user to thumbed? Where are you calling it from?
BTW, you could try to simplify all of this using associations. I'm not 100% sure I understand what you're trying to do, but it sounds like you have the following...
class Stem
has_many :thumbs
end
class User
has_many :thumbs
end
class Thumb
belongs_to :user
belongs_to :stem
end
Then you can use find though associations to get at your thumbs without resorting to direct SQL. Check out this RailsCast: http://railscasts.com/episodes/3-find-through-association
What ended up working for me first was something like this:
I added these methods to my stem model:
def thumbed_by? usr_id
Thumb.count(:conditions => {:user_id => usr_id, :stem_id => self.id}) > 0
end
def owned_by? usr_id
self.id == usr_id
end
I also added this:
attr_accessor :thumbed, owned
Then in my show action where these were needed, I added the following lines:
#stem.thumbed = #stem.thumbed_by? current_user.id
#stem.owned = #stem.owned_by? current_user.id
This works exactly how I would like (the Flex app is already interpreting it as properly), but is there a way I could shorten this?

Resources