How does CanTango work? - ruby-on-rails

I am new to Rails and I need your help.
I have this:
# config/initializers/cantango.rb
CanTango.config do |config|
config.engines.all :on
# more configuration here...
end
# app/models.User.rb
class User < ActiveRecord::Base
def roles_list
roles_rel = Role.where(:user_id=>self.id)
roles=[]
roles_rel.each do |x|
roles.push(x.name)
end
return roles #return [":reader","writer"] from database
end
end
# app/permits/reader_permits.rb
class ReaderPermit < CanTango::UserPermit
def initialize ability
super
end
protected
def permit_rules
can :read, :all
end
end
In my view I have
<%= link_to 'readddd', "/news/feed/read_full?s=#{g.id}&page_id=#{params[:page_id]}" if user_can?(:read, Newsfeed)%>
but I get an error undefined method 'user_can?' for #<#<Class:0xaf41f50>:0xaf40eac>
Please give me a very simple explanation for my situation, where and what I must write. GitHub doesn't help me.

Fallow the tutorial here:
https://github.com/kristianmandrup/cantango/wiki/Quickstart
I think you forgot this: Create and register a User model
First you must have a User model. Use the tango_user macro in order to
register a user class with CanTango. CanTango will then generate User
APIs methods such as #user_can? for the User class, admin_can? for a
registered Admin user class etc.
class User
# register as a "user class" for CanTango
tango_user
end

Related

How to extend model in rails 4.2

I have a user model
class User
def fname
#fname
end
def fname=(str)
#fname = str
end
def greeting
"Hello #{#fname}"
end
end
But I want to remove the greeting method to somewhere else so that my user model don't include the business logic.
How should I achieved that?
I try to create a module(foo.rb) in lib but its not working. Should I include in User model?
Updated Info:
I updated my code
module UserBusinessEntity
def speak(sound)
return "#{sound} is its sound"
end
def greeting
"#{self.id} Hello, #{self.fname} #{self.lname} you are #{self.age} years old"
end
end
class User < ActiveRecord::Base
include UserBusinessEntity
end
This works if both code in same file.i.e. app/models/User.rb
But I want to move the module UserBusinessEntity code to app/services/
Do I have to add require at User Model. If so I added like require UserBusinessEntity But Its gives uninitialized constant UserBusinessEntity
Just create a module like this:
module Foo
def greeting
"Hello #{self.fname}"
end
end
Then include the module in your User module:
class User
include Foo
# ...
end
Then you can call in a controller or a view
#user = User.new
#user.greeting
I believe you may use greeting to render in views or mailers. So this is a showcase of using presenter. A good article is here.
Basically, defining a presenter will be:
app/presenters/user_presenter.rb
class UserPresenter < DelegateClass(User)
def greeting
"Hello #{fname}"
end
end
There are many ways to define, the above is just basic, check out above article for detail.
Then, you can use it anywhere you want to:
#user = User.first
UserPresenter.new(user).greeting
Or even in a view
example.html.erb
<p><%= UserPresenter.new(user).greeting %><p>
Moreover, people may use concern to implement this, but with me that is not a good practice!

What's the best way to make a new model when overriding the Devise User Controller?

I'm trying to make it so that when a new User is created (through Devise), a new Household(essentially a group) model will be created if no previous Household model with that name exists.
pseudocode:
if Household.find(params[:household_name))
# allow current_user to join household
else
# create new Household model with User's household_name parameter
end
I've overwritten the base user controller from Devise::RegistrationsController with controllers/registerhousehold_controller.rb:
class RegisterhouseholdController < Devise::RegistrationsController
But I'm not sure how to implement the actual creation here. Any suggestions?
No changes in controller required as far as I see.
User.rb
after_create :create_or_join_to_household
def create_or_join_to_household
household = Household.find(params[:household_name])
if household.present?
self.join_to_household
else
Household.create(name: params[:household_name])
#or self.households.create(name: params[:household_name])
#if you have a household - user relation somehow
end
p.s.
join_to_household would be another method in your user model that will create a household_users relation.
Simple - use the before_create callback in the user model to build the object, then you'll be able to use it when you save:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :set_household, if: Proc.new {|user| user.household_id.present? }
private
def set_household
if house = Household.find(self.household_id)
#if it is set
else
#create a new houshold
end
end
end
I had to call custom method after successful sign up, on my previous task.
U also need something similar.
I'm not sure about overriding.
Try this in App. controller
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if Household.find(params[:household_name))
# allow current_user to join household
else
#create new Household model with User's household_name parameter
end
root_path
end
end
Check this

how to create a model after a devise registration

I have a user registration with an extra field called "company_name". After the user gets created, I want a Company instance to be created based on the extra field "company_name" and that user associated with the company. I've tried a few things like this:
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
super
company = Company.create(name: params[:company_name])
current_user.admin = true
current_user.company = company
current_user.save
end
def update
super
end
end
however, I don't have a current_user when trying to do the lines after I create the company. Is there a better way of doing this?
You can pass a block to the Devise controller's create that will give you the created user resource:
class RegistrationsController < Devise::RegistrationsController
CREATE_COMPANY_PARAMS = [:name]
def create
super do |created_user|
if created_user.id
company = Company.create! create_company_params
created_user.update! company_id: company.id
end
end
end
private
def create_company_params
params.require(:user).require(:company).permit(*CREATE_COMPANY_PARAMS)
end
end
There are some tough parts to handling this correctly though.
It seems that even if the user already exists, it will still call your block and pass you a user, but the user won't have an id assigned because the DB save failed. The if created_user.id check prevents a company from being created for an invalid user.
If the company already exists. The .create! will throw an exception which causes the controller to return an HTTP 422.
Utilizing the after_save callback in User model is probably suitable for this case:
# app/models/user.rb
class User < ActiveRecord::Base
...
# Execute this callback after an record is saved only on create
after_save :create_and_associate_company, on: :create
private:
def create_and_associate_company
company = self.companies.build
# Other necessary attributes assignments
company.save
end
end
Reference on other Active Record Callbacks.
You can access the newly created user using the resource variable
Here, I'm logging info only if the user was actually saved
class RegistrationsController < Devise::RegistrationsController
def create
super
if resource.persisted?
Rails.logger.info("Just created and saved #{resource}");
end
end
end

devise: instance the current_user using single table inheritance

I am using rails 3.0.9 and devise for authentication. Now I'm trying to use single table inheritance because I need to use polymorphism, so I have two classes: UserType1 and UserType2, which inherit from User class. I need that Devise instance correctly the current_user depending the type of user.
For example,
class User < ActiveRecord::Base
#devise and other user logic
end
class UserType1 < User
def get_some_attribute
return "Hello, my type is UserType1"
end
end
class UserType2 < User
def get_some_attribute
return "Hello, my type is UserType2"
end
end
In controller
class MyController < ApplicationController
def action
#message = current_user.get_some_attribute #depending the type using polymorphism
render :my_view
end
end
it's exactly what you need : http://blog.jeffsaracco.com/ruby-on-rails-polymorphic-user-model-with-devise-authentication
you need to override the sign in path method in your application controller, hope it help.
You will need to add get_some_attribute method inside User model
Module User < ActiveRecord::Base
#devise and other user logic
def get_some_attribute
#You can put shared logic between the two users type here
end
end
then, to override it in the user sub types, like this:
Module UserType1 < User
def get_some_attribute
super
return "Hello, my type is UserType1"
end
end
Module UserType2 < User
def get_some_attribute
super
return "Hello, my type is UserType2"
end
end
Then, current_user.get_some_attribute will work as you expecting, if you like to read more about overriding methods in Ruby, you can read about it here
I added super as I assumed that you have some shared logic in get_some_attribute, as it will call get_some_attribute in User model, you can remove it if you don't need it.
Good luck!

Authorization in Rails 3.1 : CanCan, CanTango, declarative_authorization?

I have looked at declarative_authorization, CanCan, and CanTango. They all are good in adding authorization to the application but I was wondering how does one add authorization to specific instance of a model i.e. a person can have a manage access in one project and only limited (read less than manage: limited update, etc) in another.
Could you please a better way? Apologies if my question sounds too trivial. It could be because I am new to RoR.
thanks,
John
As I know CanCan and declarative_authorization, and I implemented role-based authorizations with both, I recommend CanCan. Just my two cents.
Example (untested, unfortunately I cannot test here and I have no access to my code)
So let's say we have a structure like this:
class User < ActiveRecord::Base
belongs_to :role
end
class Role < ActiveRecord::Base
has_many :users
# attributes: project_read, project_create, project_update
end
Then, CanCan could look like this:
class Ability
include CanCan::Ability
def initialize(user)
#user = user
#role = user.role
# user can see a project if he has project_read => true in his role
can :read, Project if role.project_read?
# same, but with create
can :create, Project if role.project_create?
# can do everything with projects if he is an admin
can :manage, Project if user.admin?
end
end
You can find all information you need in the CanCan wiki on github. Personal recommendation to read:
https://github.com/ryanb/cancan/wiki/Defining-Abilities
https://github.com/ryanb/cancan/wiki/Defining-Abilities-with-Blocks
https://github.com/ryanb/cancan/wiki/Authorizing-Controller-Actions
Basically you just need to extend the example above to include your roles through your relations. To keep it simple, you can also create additional helper methods in ability.rb.
The main mean caveat you may fall for (at least I do): Make sure your user can do something with a model before you define what the user can't. Otherwise you'll sit there frustrated and think "but why? I never wrote the user can't.". Yeah. But you also never explicitly wrote that he can...
class User < ActiveRecord::Base
belongs_to :role
delegate :permissions, :to => :role
def method_missing(method_id, *args)
if match = matches_dynamic_role_check?(method_id)
tokenize_roles(match.captures.first).each do |check|
return true if role.name.downcase == check
end
return false
elsif match = matches_dynamic_perm_check?(method_id)
return true if permissions.find_by_name(match.captures.first)
else
super
end
end
private
def matches_dynamic_perm_check?(method_id)
/^can_([a-zA-Z]\w*)\?$/.match(method_id.to_s)
end
def matches_dynamic_role_check?(method_id)
/^is_an?_([a-zA-Z]\w*)\?$/.match(method_id.to_s)
end
def tokenize_roles(string_to_split)
string_to_split.split(/_or_/)
end
end
Usage:
user.is_an? admin
user.can_delete?

Resources