Merging parameters rails - ruby-on-rails

in my rails app, a user can select other users they'd like to join a 'party' with. I'm trying to merge the current user's ID with those that the user has selected. This is one way i've tried:
def party_params
params.require(:party).permit(:length, :game_id, user_ids: []).merge("user_ids" => ["user_ids"] + [current_user.id])
end
This comes back with :
Couldn't find all Users with 'id': (0, 17) (found 1 results, but was
looking for 2)
Doing .merge(:user_ids => [current_user.id]) instead results in the party only being created with the current user and removing all the selected users.
I've also tried moving the .merge to a function and calling it on new, but that doesn't seem to work either.
In my models a party has_and_belongs_to_many :users and likewise a user has_and_belongs_to_many :parties
Any help appreciated!
Thanks.

I would do something like this:
def party_params
params.require(:party).permit(:length, :game_id, user_ids: []).tap do |p|
p[:user_ids] << current_user.id
end
end

Related

Rails ActiveAdmin and has_many association

I am fairly new to active_admin, I was wondering if there is a way to achieve the following
I have two models
User
belongs_to :group
Group
has_many :users
I have successfully created pages in activeadmin for groups and users, now what I want is show users that belong to a certain group. I have button manage_members on groups index page which should show members of only that group. Where I can remove members from the group or add more members.
This is what I have been able to do so far
member_action :manage_members do
#group = Group.find(params[:id])
#page_title = "Manage Groups > ##{#group.name}, Edit Members"
end
and a the view app/vies/admin/groups/manage_users.html.arb
table_for assigns[:group].users do
column "Name" do |u|
u.user_id
end
column "email" do |u|
u.user.email
end
column "Created Date" do |u|
u.user.created_at
end
column "OfficePhone" do |u|
u.user.office_no
end
end
This shows the member of the groups, but I have to do all the work on this page to add edit delete a member, I cannot have active_admin filters and other cool stuff here, this is like a custom page,
Is there a way to have an index page (with all goodness of filters batch actions etc) ( like that of users ) but only showing users of a group. Something like a scoped index page which shows on users from a group and I have the same control over that page as any active admin index page ? More like the image below
rather than having to do all that work my self which currently looks like
Very new to active_admin so apologies if there something really straight forward that I am missing.
Thanks
Maybe a filter will do. That would look like (put it in the same file where you put the member_action)
filter :group, :as => :select, :collection => proc { Group.for_select }
The proc is there to make sure changes to groups (adding/removing/..) are immediately reflected to the select-list in the filter. That has to do with class caching in production.
Dont forget to put this scope in your Group model.
scope :for_select, :select => [:id, :name], :order => ['name asc']
Another way is to use scopes. If you have a field in your Group model, like a slug/label that could serve as a method header then you could do something like this in your activeadmin user register block:
Group.all.each do |group|
# note the sanitization of the Group name in the gsub
scope "#{group.name.gsub(/-/,'_')}".to_sym
end
And this in your User model:
Group.all.each do |group|
# note the sanitization of the Group name in the gsub
scope "#{group.name.gsub(/-/,'_')}".to_sym, joins(:group).where("group.name = ?",role.name)
# using joins(:group) or joins(:groups) makes a difference,
# so not sure as I have not tested, but maybe the where should be
# ....where("groups.name = ....
end
It should give you nice buttons above your index views like here: http://demo.activeadmin.info/admin/orders
If you want this for a has_and_belongs_to_many relation, I suggest you take a look at this Rails3 Active Admin - How to filter only records that meet all checked items in collection
Good luck!

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

Ruby on Rails: how do you write a find statement that sums up a bunch of values if an object belongs to another object?

Lets say Users have BankAccounts which have a balance value.
So, how do I generate an array that Users and the total value of all their BankAccounts' values added together?
I'm not sure if this is quite what you want, but you can do something like:
ActiveRecord::Base.connection.select_all("select user_id, sum(balance) from accounts group by user_id;")
This will give you an array of user_ids and balances from the accounts table. The advantage of doing it this way is that it comes down to only one SQL query.
You'll want to do something like this. I don't believe it's possible to use #sum via an association.
class User
def net_worth
BankAccount.sum(:balance, :conditions => ["user_id = ?", self.id])
end
end
Edit
I see a reference to a #sum in AssociationCollection, so try this:
class User
def net_worth
self.bank_accounts.sum(:balance)
end
end
(I haven't tested this code)
First you need to find the users you want so I'll just assume you want all users.
#users = User.all
Then you need to take the array and collect it with only the elements you want.
#users.collect! {|u| [u.name, u.bank_account_total_value]}
For this kinda attribute I would set it in the model assuming you have has_many :transactions as an association
Class User
has_many :transactions
def bank_account_total_value
total = 0
self.transactions.each do |t|
total += t.amount
end
end
end

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?

Best practice: How to split up associations-functions in controllers with equal-access models

I have 2 equal-access models: Users and Categories
Each of these should have the standard-actions: index, new, create, edit, update and destroy
But where do I integrate the associations, when I want to create an association between this two models?
Do I have to write 2 times nearly the same code:
class UsersController << ApplicationController
# blabla
def addCategory
User.find(params[:id]).categories << Category.find(params[:user_id])
end
end
class CategoriessController << ApplicationController
# blabla
def addUser
Category.find(params[:id]).users << User.find(params[:user_id])
end
end
Or should I create a new Controller, named UsersCategoriesController?
Whats the best practice here? The above example doens't look very DRY.... And a new controller is a little bit too much, I think?
Thanks!
EDIT:
I need to have both of these associations-adding-functions, because f.e.
#on the
show_category_path(1)
# I want to see all assigned users (with possibility to assign new users)
and
#on the
show_user_path(1)
#I want to see all assigned categories (with possibility to assign new categories)
EDIT:
I'm taking about a HBTM relationship.
If you have a situation where you need to do this with has_and_belongs_to_many, you could take the approach you are currently using, or you could build this into your existing update actions.
When you add a habtm relationship, you will get an additional method on your classes...
class User < ActiveRecord::Base
has_and_belongs_to_many :categories
end
With this, you can do this:
user = User.find(params[:id])
user.category_ids = [1,3,4,7,10]
user.save
The categories with those ids will be set. If you name your form fields appropriately, the update can take care of this for you if you want to use checkboxes or multiselect controls.
If you need to add them one at a time, then the methods you've built in your original post are reasonable enough. If you think the repetition you have is a code smell, you are correct - this is why you should use the approach I outlined in my previous answer - an additional model and an additional controller.
You didn't mention if you are using has_and_belongs_to_many or if you are using has_many :through. I recommend has_many :through, which forces you to use an actual model for the join, something like UserCategory or Categorization something like that. Then you just make a new controller to handle creation of that.
You will want to pass the user and category as parameters to the create action of this controller.
Your form...
<% form_tag categorizations_path(:category_id => #category.id), :method => :post do %>
<%=text_field_tag "user_id" %>
<%=submit_tag "Add user" %>
<% end %>
Your controller...
class CategorizationsController < ApplicationController
def create
if Categorization.add_user_to_category(params[:user_id], params[:category_id])
...
end
end
then your categorization class...
class Categorization
belongs_to :user
belongs_to :category
def self.add_user_to_category(user_id, category_id)
# might want to validate that this user and category exist somehow
Categorization.new(:user_id => user_id, :category_id => category_id)
Categorization.save
end
end
The problem comes in when you want to send the users back, but that's not terribly hard - detect where they came from and send them back there. Or put the return page into a hidden field on your form.
Hope that helps.

Resources