Accessing an external table from a Rails application - ruby-on-rails

Rails 3.2
I am using the API gem. What the client wants, is to keep the table where he wants to whitelist the email addresses that can be used to access the API, in a seprate table, that he only can access through phpmyadmin.
This would be a single table:
api_users
With a single column: email (in addition to id, created_at, updated_at)
The email addresses that would go in this table, also exist in the users table for the rails application.
If I create a model: models/api_user.rb:
class ApiUser< ActiveRecord::Base
attr_accessible :email
And, in my models/api_ability.rb, I add the following:
class ApiAbility
include CanCan::Ability
def initialize(user, params = {})
user ||= User.new
if ApiUser.find_by_email(user.email)
can :manage, :api
end
end
end
Will this work?

That sounds absolutely doable. You might want to add something like
def readonly?
true
end
to the ApiUser class to make sure no one will try to create instances of it from within Rails. But apart from that I don't see any reason not to do it that way given the clients requirements.

Related

manage roles in rails

I want create roles in my project. Each user can be: admin, registered or demo. Each role see different things.
How can I do that? What is the best gem to do roles?
This is a example in 'bad programming" of what I want:
def index
if current_user.role[:name] == 'admin'
#installations = Installation.all
elsif current_user.role[:name] == 'registered'
#installations = current_user.installations
elsif current_user.role[:name] == 'demo'
#installations = current_user.installations.first
else
end
end
Some gems that might be interesting for you :
rolify
role_model
If you decide to implement it yourself, then within some page you might want to change the content, for that you might want to do something like this :
Add a role to the user model using a migration :
class AddRoleToUsers < ActiveRecord::Migration
def change
add_column :users, :role, :string, default: :demo
end
end
Then in your app you can use it as follows:
def index
case current_user.role
when :admin
#installations = Installation.all
when :registered
#installations = current_user.installations
else
#installations = current_user.installations.first
end
end
You can also simply create a boolean admin for instance.
What you might want to do also is create some methods in your model so that you can call current_user.admin? or current_user.registered? . You can do that by doing (if you chose to use a string to store the role):
class User < ActiveRecord::Base
def admin?
self.role == "admin"
end
def registered?
self.role == "registered"
end
end
One advantage I see of having a role stored in a string is that if you have 5 roles for instance then you do not have 4 booleans (as when you store admin in a boolean) but only one string. On the long run you might want to store actually a role_id instead of a string and have a separate role model.
An excellent alternative pointed out by Jorge de Los Santos (another answer) is to use enum :
class User < ActiveRecord::Base
enum role: [:demo, :admin, :registered]
end
It is an excellent alternative because it will automagically add the methods described above such as current_user.admin? without hard coding them.
With your roles, you might want to do some authorization (admins can have access to specific pages, demo users are restricted to only a subset of pages, etc.). For this, you can use the gem called cancancan. You can look at this railscast to learn more about it. Also, you can have some infos here : How to use cancancan? .
There are plenty of solutions available to you.
Starting by gems:
https://github.com/RolifyCommunity/rolify
https://github.com/martinrehfeld/role_model
By using Devise architecture (in case you use it):
https://github.com/plataformatec/devise/wiki/How-To:-Add-a-default-role-to-a-User
By using enums in rails 4:
class AddRolesToUser < ActiveRecord::Migration
#add_column 'role', :integer, default: 0 to the users table
end
class User < ActiveRecord::Base
enum role: [:demo, :admin, :registered]
end
That will enable role methods.
user = User.find(1)
user.role #:demo
user.admin? #false
user.registered? #false
And consequently:
if user.admin?
#somethig
elsif user.registered?
#another something
else
#another another something.
And last but not least, what you are searching is not the manage roles solution, is the manage permissions solutions:
https://github.com/ryanb/cancan
Add a boolean, :admin to your User model.
class AddAdminToUsers < ActiveRecord::Migration
def change
add_column :users, :admin, :boolean, deafult: false
end
end
Create a method for a registered user to separate them from demo users, such as verifying their email, providing a home address and phone number, filling out a profile, etc. This is up to you though, first you need to decide how a registered and demo user should be different.
The CanCan gem adds authorization to your project, and is especially useful if you want to implement multiple roles with differing abilities. When used with an authentication system like devise, you get a full suite of capability for your site.
You're in full control of what roles you want to define and what abilities they have. CanCan manages tracking, assignment, and querying of roles, and then gets out of your way to let you build what you need.
You can find the CanCan gem in Github: https://github.com/ryanb/cancan
It's simple to use, and the documentation is straightforward and easy to follow.

Create User Account Settings Page in Ruby on Rails with devise

I am new to Ruby on Rails and I have created a project that contains a User table (generated by devise) and a AccountSetting table that contains user specific account settings (this table has a foreign key that relates to the id in the User table thus each User has zero or one AccountSettings). I have my seed data working fine, and I can seed the database with users that have user specific account settings. The User table is related to the AccountSetting table with a "has_one :accountsetting" and the AccountSettings table "belongs_to :user". This all works and makes sense. However, I have a method called "show_user_setting" in my UserSettings controller, and I do not know how to ONLY SHOW the account settings for that specific authenticated user.
So, how can I only display the user setting for the currently logged in user? Again, I am using devise.
My general idea of how to do this would be something like this. However I know this is incorrect, but for the purpose of an explanation, here it is.
def show_user_setting
#setting = AccountSetting.find(current_user)
end
My idea is that the #setting will contain the setting for the currently logged in user. Thanks in advance!
You should do this:
#app/models/account_setting.rb
class AccountSetting < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
class User < ActiveRecord::Base
has_one :account_setting
end
This will allow you to call the following:
#setting = current_user.account_setting
Our Setup
For what it's worth, we do something similar:
#app/models/user.rb
class User < ActiveRecord::Base
before_create :build_profile #-> builds a blank profile on user create
has_one :profile
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
This allows us to put all sorts of different options inside the profile model (we have homepage etc):
The important thing to note here is that the above allows you to delegate various methods to the profile model, allowing you to call the following:
current_user.profile_name
current_user.profile_signin_redirect?
current_user.profile_avatar
etc
Have you tried
def show_user_setting
#setting = AccountSetting.find_by(user_id: current_user.id)
end
The way .find() works is it searches the model for the id passed. So, the way you currently have it is your going to try to search for the id of the model, when you want to find the foreign key. So use Model.find_by(column_name: param). You'll what to change user_id: to the column name of what you're storing the foreign key in, I'm just assuming it's something similar to that.
I'm guessing the show_user_setting function is part of a controller, if it is on a model then read this: accessing devise current_user within model
to set the #setting variable you should be able to do this
#setting = AccountSetting.find(user_id: current_user.id)
or
#setting = AccountSetting.find(user: current_user)

how to run a one-time database change on a single user

I have Customer and each customer has_many Properties. Customers belong to a Company.
I'm trying to add a certain Property to each one of a single Company's Customers. I only want this change to happen once.
I'm thinking about using a migration but it doesn't seem right to create a migration for a change that I only ever want to happen once, and only on one of my users.
Is there a right way to do this?
You can just use rails console.
In rails c:
Company.where(conditions).last.customers.each do |customer|
customer.properties << Property.where(condition)
customer.save!
end
Validation
Depending on how you're changing the Customer model, I'd include a simple vaidation on the before_update callback to see if the attribute is populated or not:
#app/models/Customer.rb
class Customer < ActiveRecord::Base
before_update :is_valid?
private
def is_valid?
return if self.attribute.present?
end
end
This will basically check if the model has the attribute populated. If it does, it means you'll then be able to update it, else it will break
--
Strong_Params
An alternative will be to set the strong_params so that the attribute you want to remain constant will not be changed when you update / create the element:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
private
def strong_params
params.require(:model).permit(:only, :attributes, :to, :update)
end
end
It would be much more helpful if you explained the context as to why you need this type of functionality - that will give people the ability to create a real solution, instead of proposing ideas

In my rails app, how can I automatically attach a userID when a new product is created?

I have a rails app, with two separate DB tables, users and products. A user has_many products, and a product belongs_to a user.
When I create a product, I want it to automatically add the user_id to the user_id database column in the products table. What changes to my mvc do I need to make to ensure that the correct user_id is added when a new product is created?
You can scope the creation of the new product through the user.
For example, instead of this:
Product.create(params[:product])
you do this:
current_user.products.create(params[:product])
where "current_user" is the user creating the product.
Just as a suggestion, you may want to go back and accept the answers to some of your previous questions, which will improve your response rate and increase the likelihood someone will answer your questions in the future.
There are a few ways to do this, one approach:
Create current user function
class ApplicationController < ActionController::Base
private
# Finds the User with the ID stored in the session with the key
# :current_user_id This is a common way to handle user login in
# a Rails application; logging in sets the session value and
# logging out removes it.
def current_user
#_current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
end
http://guides.rubyonrails.org/action_controller_overview.html#session
Make sure to be cognizant of security concerns. A gem like Devise can also help.
Add to products controller
class ProductsController < ApplicationController
def create
current_user.products.create! params[:product] # make sure attr_accessible is setup on products
end
end

RoR: Different user roles for each new created record?

I want to make a record management system. The system will have 4 different user roles: Admin, Viewer, Editor and Reviewer.
While the first two are easy to implement using gems such as cancan and declarative authorization, the other two are not so simple.
Basically each new record is created by an Admin (only an Admin can create new records), and should have its own separate Editor and Reviewer roles. That is, a user can be assigned many different roles on different records but not others, so a user might be assigned Editor roles for Record A and C but not B etc.
Editor: can make changes to the record, and will have access to specific methods in the controller such as edit etc.
Reviewer: will be able to review (view the changes) made to the record and either approve it or submit comments and reject.
Viewer: Can only view the most recent approved version of each record.
Are there any ways of handling such record-specific user roles?
This can be accomplished without too much effort with the cancan gem and a block condition. A block condition checks for authorization against an instance. Assuming your Record class had an editors method that returns an array of authorized editors the cancan ability for updating a Record might look something like this:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
...
can :update, Record do |record|
record.editors.include?(user)
end
...
end
end
See "Block Conditions" on the CanCan wiki:
https://github.com/ryanb/cancan/wiki/Defining-Abilities
Update
Storing which users have which access to which records could be done many ways depending on your specific needs. One way might be to create a model like this to store role assignments:
class UserRecordRoles < ActiveRecord::Base
# Has three fields: role, user_id, record_id
attr_accessible :role, :user_id, :record_id
belongs_to :user_id
belongs_to :record_id
end
Now create a has_many association in the User and Record models so that all role assignments can be easily queried. An editors method might look like this:
class Record < ActiveRecord::Base
...
has_many :user_record_roles
def editors
# This is rather messy and requires lot's of DB calls...
user_record_roles.where(:role => 'editor').collect {|a| a.user}
# This would be a single DB call but I'm not sure this would work. Maybe someone else can chime in? Would look cleaner with a scope probably.
User.joins(:user_record_roles).where('user_record_roles.role = ?' => 'editor')
end
...
end
Of course there are many many ways to do this and it varies wildly depending on your needs. The idea is that CanCan can talk to your model when determining authorization which means any logic you can dream up can be represented. Hope this helps!

Resources