I'm having a strange issue.
I have a number of models and associations that work perfectly together but when I try to introduce the current_user I get:
ActiveRecord::ConfigurationError at /dashboard
Message Association named 'game' was not found; perhaps you misspelled it?
Here's some code that works:
Controller:
def index
users = current_user.followed_users.collect { |user| user.id }
#userupdates = Userupdate.for_users(users, params[:page])
end
View:
<% #userupdates.each do |userupdate| %>
Things and stuff
<% end %>
But when I try to make the page display content from followed_users AND the current_user like so..
def index
users = current_user.followed_users.collect { |user| user.id }
users.push(current_user.id)
#userupdates = Userupdate.for_users(users, params[:page])
end
...I get the error above.
Some of the relavent model code:
class Userupdate < ActiveRecord::Base
belongs_to :user
belongs_to :game
class User < ActiveRecord::Base
has_many :userupdates
class Game < ActiveRecord::Base
has_many :userupdates
ActiveRecord::ConfigurationError is explained as below in rails api.
Raised when association is being configured improperly or user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
So I thought the problem is your association configured. You may check again the association, like whether model Userupdate has user_id and game_id.
And the current_user issue, maybe you should check your query sql to see whether your includes works. If works, it should do the outer join between userupdates and users, userupdates and games, and you'll see loading users and games after loading userupdates in log. And current_user maybe the only user who has the userupdates whose belonging game exists.
All my opinions, hope this can help.
Related
I am using Devise for users.
User.rb
belongs_to shop
has_many tasks
Show.rb
has_many users
has_many tasks
Task.rb
belongs_to user
belongs_to shop
when I create a new task:
current_user.tasks.create(...)
the shop_id gets the value of nil, when I need to be the same shop_id as the user.
when I create a new task
current_user.shop.tasks.create(...)
I get the user_id as nil but gets the right value for the shop_id.
what am I am missing?
thanks in advance.
When
current_user.tasks.create(...)
is run rails association would not know that it has to populated shop_id unless you explicitly send it
current_user.tasks.create(shop_id: current_user.shop.id)
Same the other way around. You can use a better modeling for this case with polymorphic association between user, shops and tasks. More details and example can be found here.
See
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Do not think this is related to devise.
In current_user.shop.tasks.create(...) you're calling create directly on the tasks collection for a singular shop. This is effectively equivalent to:
Shop.find_by(user_id: current_user.id).tasks.create(...)
Shops can possibly have more than one user, so there's nothing explicit in that statement that the newly created task should belong to current_user.
I think the simplest solution is to create the task by itself, explicitly setting both foreign keys:
Task.create(shop_id: current_user.shop_id, user_id: current_user.id)
Though you'd have to reload your user and shop references to pick up the newly associated task.
If you want something more automatic, consider using an association callback on has_many :tasks within user where shop_id for a Task is set from the user's shop_id:
class user < ActiveRecord::Base
belongs_to :shop
has_many :tasks, before_add: :assign_to_shop
def assign_to_shop(task)
task.shop = self.shop
...
end
end
Devise current_user method returns same object of user.
# simple example
def sign_in
session[:current_user] = User.find_by_email(params[:email])
end
def current_user
session[:current_user]
end
If user signed in, then current_user method should work, as like in right below.
#1
current_user.tasks.create(...)
#2 you can also like this
t = Task.new(...)
t.user_id = current_user.id
t.save
You can play in rails console, easy to understand it.
current_user = User.first
current_user.tasks.create(...)
I am trying to figure out the best way to accomplish my problem. I've got a pages table, and a user_types table. I am trying to specify multiple user types on a page. They will act as permission groups. I need to do this twice however. Once for read permissions, and once for edit permissions. Here is an example:
Home page has 3 user types that can read it - admin, super admin, public
It has 2 user types that can edit it - admin, super admin
I have one user_types table:
admin
super admin
public
etc
I have created two mapping tables (one for read, and one for edit):
pages_user_read_types
pages_user_edit_types
they both have page_id, and user_type_id
Is there a better way to accomplish this? If this is the best way, I need help figuring out the relationships for the models. I have this for one relationship
has_and_belongs_to_many :user_types, :join_table => :pages_user_read_types
How do i specify two relationships for seperate fields?
Thanks
The HABTM relationship in Rails has seemed to fall out of favor over the last couple of years with Rails developers to the has_many :through relationship. The only time you should use HABTM is when you have no need for any additional information about the relationship between two models. In your case, you are trying to emulate this by creating two HABTM relationships when you could effectively accomplish by having a join model with a editable attribute.
In code, it would look something like this:
class Page < ActiveRecord::Base
has_many :page_permissions
has_many :user_types, :through => page_permissions
def editable_user_types
page_permissions.includes(:user_types).where(:editable => true).map(&:user_type)
end
def read_only_user_types
page_permissions.includes(:user_types).where(:editable => false).map(&:user_type)
end
end
class PagePermission < ActiveRecord::Base
belongs_to :page
belongs_to :user_type
# When you create this model, you should have a boolean attribute for editable
end
class UserType < ActiveRecord::Base
has_many :page_permissions
has_many :pages, :through => :page_permissions
end
I think following this approach will allow you to consolidate to one join table which will be better in the future if you need to add additional attributes to the relationship (PagePermission) between Page and UserType.
At the very least, you probably want to add a Permission model. If it ever gets more complicated than what you've described, I would also recommend using CanCan.
class Permission < ActiveRecord::Base
#table is id, page_id, user_type_id, and permission_type (string).
belongs_to :page
belongs_to :user_type
end
In your controller, you can construct a filter chain like this:
class PagesController < ApplicationController
before_filter :load_page
before_filter :authorize_view!, only: [ :show ]
before_filter :authorize_edit!, only: [ :edit ]
def show
end
def edit
end
private
def load_page
#page = Page.find(params[:id])
end
def authorize_view!
if !#page.permissions.where(user_type_id: current_user.user_type_id, permission_type: "view").exists?
flash[:notice] = "You do not have permission to view that page."
redirect to root_path
end
end
def authorize_edit!
if !#page.permissions.where(user_type_id: current_user.user_type_id, permission_type: "edit").exists?
flash[:notice] = "You do not have permission to edit that page."
redirect to root_path
end
end
end
(This assumes you have a current_user method in your app).
I have an application with Users, Posts and Comments. Users has_many Posts, Posts has_many Comments and Comments belong_to Users and Posts.
In my view template I'm looping over the Comments like this to get the username of the commenter:
User.find(comment.user_id).name
How efficient is this if I'm doing this for every Comment per Post ?
I could imagine that it's maybe faster to store the username in a separate row in the Comments table, but duplicating the data seems just wrong.
Am I being paranoid and is ActiveRecord doing some caching magic or is there a better way of doing something like this ?
You can preload the users in the initial query.
Seeing as you have the association setup between User and Comment you can access the user via the association.
Let's assume you have a controller method:
def show
#posts = Post.limit(10).includes(:comments => :user)
end
In your view as you loop over #comments:
#posts.each do |post|
# show post stuff
post.comments.each do |comment|
comment.user.name
end
end
Revised
If you have association as below you can access user from comment object itself comment.user
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Eager loading of associations
class Post < ActiveRecord::Base
has_many :comments
# ...
end
class Comment < ActiveRecord::Base
belongs_to :user
end
comments = #post.comments.includes(:user)
It will preload users.
SQL will look like:
SELECT * FROM `comments` WHERE `comments`.`post_id` = 42;
SELECT * FROM `users` WHERE `users`.`id` IN (1,2,3,4,5,6) # 1,2,3,4,5,6 - user_ids from comments
If performance is an issue for you, using a none relational DB, like Mongodb is the way to go.
If you still want to use ActiveRecord, either you use eager loading with Post.comments.include(:user) (which will load unused users info - and that's not great), or you can use caching techniques.
I find it OK to cache the user.name in the comments table, as you suggested, as long as you control the changes that can occur. You can do that by setting callbacks in your User model:
after_save do |user|
Comment.where(user_id: user.id).update_all(:username, user.name)
end
This technique is sort of a DB caching, but, of course, you can cache HTML fragments. And a comment block is a good to cache HTML block.
What is the way to implement "business rules" in Rails?
Let us say I have a car and want to sell it:
car = Cars.find(24)
car.sell
car.sell method will check a few things:
does current_user own the car?
check: car.user_id == current_user.id
is the car listed for sale in the sales catalog?
check: car.catalogs.ids.include? car.id
if all o.k. then car is marked as sold.
I was thinking of creating a class called Rules:
class Rules
def initialize(user,car)
#user = user
#car = car
end
def can_sell_car?
#car.user_id == #user.id && #car.catalogs.ids.include? #car.id
end
end
And using it like this:
def Car
def sell
if Rules.new(current_user,self).can_sell_car
..sell the car...
else
#error_message = "Cannot sell this car"
nil
end
end
end
As for getting the current_user, I was thinking of storing it in a global variable?
I think that whenever a controller action is called, it's always a "fresh" call right? If so then storing the current user as a global variable should not introduce any risks..(like some other user being able to access another user's details)
Any insights are appreciated!
UPDATE
So, the global variable route is out! Thanks to PeterWong for pointing out that global variables persist!
I've now thinking of using this way:
class Rules
def self.can_sell_car?(current_user, car)
......checks....
end
end
And then calling Rules.can_sell_car?(current_user,#car) from the controller action.
Any thoughts on this new way?
I'd use the following tables:
For buyers and sellers:
people(id:int,name:string)
class Person << ActiveRecord::Base
has_many :cars, :as => :owner
has_many :sales, :as => :seller, :class_name => 'Transfer'
has_many :purchases, :as => :buyer, :class_name => 'Transfer'
end
cars(id:int,owner_id:int, vin:string, year:int,make:string,model:string,listed_at:datetime)
listed_at is the flag to see if a Car is for sale or not
class Car << ActiveRecord::Base
belongs_to :owner, :class_name => 'Person'
has_many :transfers
def for_sale?
not listed_at.nil?
end
end
transfers(id:int,car_id:int,seller_id:int,buyer_id:int)
class Transfer << ActiveRecord::Base
belongs_to :car
belongs_to :seller, :class_name => 'Person'
belongs_to :buyer, :class_name => 'Person'
validates_with Transfer::Validator
def car_owned_by_seller?
seller_id == car.owner_id
end
end
Then you can use this custom validator to setup your rules.
class Transfer::Validator << ActiveModel::Validator
def validate(transfer)
transfer.errors[:base] = "Seller doesn't own car" unless transfer.car_owned_by_seller?
transfer.errors[:base] = "Car isn't for sale" unless transfer.car.for_sale?
end
end
First, the standard rails practice is to keep all business logic in the models, not the controllers. It looks like you're heading that direction, so that's good -- BUT: be aware, there isn't a good clean way to get to the current_user from the model.
I wouldn't make a new Rules model (although you can if you really want to do it that way), I would just involve the user model and the car. So, for instance:
class User < ActiveRecord::Base
...
def sell_car( car )
if( car.user_id == self.id && car.for_sale? )
# sell car
end
end
...
end
class Car < ActiveRecord::Base
...
def for_sale?
!catalog_id.nil?
end
...
end
Obviously I'm making assumptions about how your Catalog works, but if cars that are for_sale belong_to a catalog, then that method would work - otherwise just adjust the method as necessary to check if the car is listed in a catalog or not. Honestly it would probably be a good idea to set a boolean value on the Car model itself, this way users could simply toggle the car being for sale or not for sale whenever you want them to ( either by marking the car for sale, or by adding the car to a catalog, etc. ).
I hope this gives you some direction! Please feel free to ask questions.
EDIT: Another way to do this would be to have methods in your models like:
user.buy_car( car )
car.transfer_to( user )
There are many ways to do it putting the logic in the object its interacting with.
I would think this would a prime candidate for using a database, and then you could use Ruby to query the different tables.
You might take a look at the declarative authorization gem - https://github.com/stffn/declarative_authorization
While it's pre-configured for CRUD actions, you can easily add your own actions (buy, sell) and put their business logic in the authorization_rules.rb config file. Then, in your controllers, views, and even models!, you can easily ask permitted_to? :buy, #car
I'm doing something similar with users and what they can do with photo galleries. I'm using devise for users and authentication, and then I set up several methods in the user model that determine if the user has various permissions (users have many galleries through permissions) to act on that gallery. I think it looks like the biggest problem you are having is with determining your current user, which can be handled quite easily with Devise, and then you can add a method to the user model and check current_user.can_sell? to authorized a sale.
Hi (huge Rails newbie here), I have the following models:
class Shop < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :title, :user_id, :message => "is already being used"
end
and
class User < ActiveRecord::Base
has_one :shop, :dependent => :destroy
end
When I'm about to create a new shop, I get the following error:
private method `create' called for nil:NilClass
This is my controller:
#user = current_user
#shop = #user.shop.create(params[:shop])
I've tried different variations by reading guides and tutorials here and there, but I'm more confused than before and can't get it to work. Any help would be greatly appreciated.
A more concise way to do this is with:
#user.create_shop(params[:shop])
See methods added by has_one in the Ruby on Rails guides.
First of all, here is how to do what you want:
#user = current_user
#shop = Shop.create(params[:shop])
#user.shop = #shop
Now here's why your version did not work:
You probably thought that this might work because if User had a has_many relation to Shop, #user.shops.create(params[:shop]) would work. However there is a big difference between has_many relations and has_one relations:
With a has_many relation, shops returns an ActiveRecord collection object, which has methods that you can use to add and remove shops to/from a user. One of those methods is create, which creates a new shop and adds it to the user.
With a has_one relation, you don't get back such a collection object, but simply the Shop object that belongs to the user - or nil if the user doesn't have a shop yet. Since neither Shop objects nor nil have a create method, you can't use create this way with has_one relations.
Two more ways if you want save instead of create:
shop = #user.build_shop
shop.save
shop = Show.new
shop.user = #user
shop.save
Just to add to above answers -
#user.create_shop(params[:shop])
Above syntax creates new record but it subsequently deletes similar existing record.
Alternatively, if you do not want to trigger delete callback
Shop.create(user_id: user.id, title: 'Some unique title')
This thread might be helpful. Click here