declarative_authorization problem with creating new user - ruby-on-rails

I used declarative_authorization for my app and had problem with creating new user.
my User model code:
class User < ActiveRecord::Base
ROLE_TYPES = ["admin", "user", "guest"]
validates_inclusion_of :roles, :in => ROLE_TYPES
def role_symbols
#role_symbols ||= (roles || []).map{|r| r.to_sym}
end
my view code:
<% form_for(#user) do |f| %>
...
<p>
<%= f.label :roles %><br />
<%= f.select :roles, User::ROLE_TYPES, :prompt => "Select a role" %>
</p>
<%= f.submit 'Add User' %>
<% end %>
every time i tried to create a new user and select the role from the drop-down list, the view complaint:
Roles is not included in the list
from the output of the script/server, i can see the roles was actually set:
"user"=>{"name"=>"kc", "password_confirmation"=>"kc", "roles"=>"guest", "password"=>"kc", "email"=>"kc#one.com"}
can anyone tell me what's wrong? why the validation wont' pass?

Is it possible that you've got attr_accessible attributes on the user to prevent mass assignment of certain attributes and that :roles isn't in there? You would get a warning about this in your logs though. The default User class generated by restful_authentication does include the attr_accessible call so it may be there without you having added it if you are using that authentication plugin too.
Is there definitely a roles attribute of the right type for users? It looks like you're expecting roles to be a single string from your form but in the code from declarative_authorization you've got (roles || []).map which suggests that that part of the code at least is expecting an array of roles.

Related

Rails f.select Save Id an Name at the same time

I have a form with the f.select option. I want to add the Company ID and the Company Name when the company name is selected from the options. Is it possible to add multiple attributes in the f.select line of code? This is how my code looks:
<div class="field">
<%= f.label 'Client' %>
<%= f.select :client_id, Client.all.collect { |c| [ c.companyName, c.id ] } %>
I want to also assign the companyName to my :companyName attribute, but the id is the only thing that is being saved right now.
Thanks.
I think you can use associations for this. You should have 2 models as below.
class Company < ApplicationRecord
has_many :clients
end
class Client < ApplicationRecord
belongs_to :company
end
Your should use variable instead in views for rendering all clients list with this
<%= f.select :client_id, #clients.collect { |client| [ client.name, client.id] } %>
Controller:
#clients = Client.all
On Submit the client_id will be passed to controller. You can then find the company associated with the client and other logic.
client = Client.find(params[:client_id])
company = client.company
# etc ..
In the controller action where your form submits you can write before saving or updating record.
params[:your_object][:companyName]=Client.find_by(id: params[:your_object][:client_id]).companyName
Remember to a add params[:your_object][:companyName] to your strong params. e.g.
params.require(:your_object).permit(:client_id, ... , :companyName)
Before anything be sure your client_id is in same object and permitted and apply same to companyName attribute
Hope This Helps

How do I call the name of a user when they belong to a model

I have two models, Chasing and User, a chasing belongs_to :user and a user has_many :chasings.
I created a migration for linking the two models together:
class AddUsersToChasings < ActiveRecord::Migration
def change
add_reference :chasings, :user, index: true, foreign_key: true
end
end
I have a controller for creating new users which I then want to be able to assign to chasings. I currently have this code in my chasings form for selecting the user:
<%= f.select :user_id, options_for_select(User.all.map {|c| [c.name, c.id]}), { :include_blank => "Please select user"}, {:class => "form-control"} %>
This seems to do the trick, after calling Chasing.first in rails console I can see the chasing now has user_id relevant to the user I picked. I can also run Chasing.first.user.name to give me the name of the user who is associated with the chasing. I'm wanting to show this name in my index view, the code I currently have for this is:
ChasingsController:
def index
#chasing = Chasing.all
end
Index view:
<% #chasing.each do |chasing| %>
<%= chasing.user %>
<% end %>
This shows a random string (seems to change every time I update a chasing - #<User:0xf5b0ba8> for example). when I change this to chasing.user.name I get 'undefined method `name' for nil:NilClass'.
Is there a way I can call the name for my view?
EDIT:
As per NickM's comment below I had chasings without users assigned to them causing active record to throw the error.
Looks like you have some Chasing objects in your database without user_ids. You can test by doing <%= chasing.user.name if chasing.user %>

Formtastic pre-check few checkboxes

I'm trying to manually tell formtastic to check a few checkboxes. #some_array currently has an element called checked which exists for each member.
= f.input :cboxes, label: "CBoxes", as: :check_boxes,
collection: #some_array.map { |a| [a[:name], a[:id]] }
I've tried to set the input_html to { checked: 'checked' } (How to pre-check checkboxes in formtastic) but this checks all checkboxes, not just the select few that I want.
The contents of #some_array are coming via an API, and I can't change the database structure (Ruby on Rails + Formtastic: Not checking checkboxes for multiple checked answers)
Suggestions?
If you are editing an ActiveModel, you don't need to "manually select checkboxes".
Let's consider a simple example with a single User model which has fields username and roles. Roles field is a string column, which Rails serializes as an Array. It might also be has_many relation to other ActiveModel, but we assume it's an Array for simplicity.
User is defined in user.rb:
class User < ActiveRecord::Base
serialize :roles, Array
end
Now you can "assign manually" desired roles to User in your controller:
#user = User.new(username: 'dimakura', roles: ['admin', 'editor'])
and define form in your view:
<%= semantic_form_for #user do |f| %>
<%= f.input :username %>
<%= f.input :roles, as: :check_boxes, collection: ['owner', 'admin', 'editor', 'viewer'] %>
<% end %>
In given example only "admin" and "editor" roles will be pre-selected in form. The "owner" and "viewer" role won't be selected.
Update Official documentation states:
Formtastic, much like Rails, is very ActiveRecord-centric.
But actually it's not a big challenge to create ActiveRecord-compatible model yourself. An example of doing this can be found in this blog post.

RecordNotFound with accepts_nested_attributes_for and belongs_to

I get
ActiveRecord::RecordNotFound: Couldn't find Client with ID=3 for Order with ID=
when trying to submit an Order form for an existing client. This happens through the form or the console by typing:
Order.new(:client_attributes => { :id => 3 })
payment_form.html.erb:
<%= semantic_form_for #order, :url => checkout_purchase_url(:secure => true) do |f| %>
<%= f.inputs "Personal Information" do %>
<%= f.semantic_fields_for :client do |ff| %>
<%= ff.input :first_name %>
<%= ff.input :last_name %>
<!-- looks like semantic_fields_for auto-inserts a hidden field for client ID -->
<% end %>
<% end %>
<% end %>
Order.rb:
class Order < ActiveRecord::Base
belongs_to :client
accepts_nested_attributes_for :client, :reject_if => :check_client
def check_client(client_attr)
if _client = Client.find(client_attr['id'])
self.client = _client
return true
else
return false
end
end
end
The reject_if idea came from here but I logged the method and it's not even being called! It doesn't matter what its name is!
Note: Feb 2020
Since I'm starting to get downvotes on this 8 years later, adding this note. While this was the original solution I went with 8 years ago, a better one has been proposed by MatayoshiMariano (5 years after my OP).
My Original Fix
Fixed the issue by overloading the client_attributes= method, as described here:
def client_attributes=(client_attrs)
self.client = Client.find_or_initialize_by_id(client_attrs.delete(:id))
self.client.attributes = client_attrs
end
If you only want a new Order with an existing client, without modifying the client, you need to assign the id.
Order.new(client_id: 3)
This is another way to do this without overloading the client_attributes= method and cleanest
The new Order now has the client with ID 3
If you also want to update ant client's attributes you must add the client_attributes, for example:
Order.new(client_id: 3, client_attributes: { id: 3, last_order_at: Time.current })
See https://github.com/rails/rails/issues/7256 from 2012.
If you have has_many relationship, this will work. Tested on Rails 6.0.2
def clients_attributes =(attributes)
# Get IDs for any clients that already exist.
client_ids = attributes.values.map { |a| a[:id] }.compact
# Now find them all and move them to this section.
clients << Client.find(client_ids)
# Update them with standard `accepts_nested_attributes_for` behaviour.
super attributes
end
Had the same error creating a new Thing for existing model with has_many and belongs_to relations.
Fixed it by adding a hidden field for the id of the existing model, for instance User, to the form.
= form.input :user_id, as: :hidden
Then new Thing was created without the error.

rails form - checkboxes, FK and datetime -> how to store those attributes in the db?

(rails 2.2.2)
I have 2 models, user and subscription. Each user can have one ore more subscriptions (= premium services). Below the attributes:
user: id, username, ...
subscription: id, user_id (FK), type, started_at, ended_at
The classes:
class User < ActiveRecord::Base
..
has_many :subscriptions, :dependent => :destroy
..
end
class Subscription < ActiveRecord::Base
belongs_to :user, :foreign_key => :user_id
end
Now I want to make the UI part where existing users can subscribe in their account for the premium services. Therefore I wanted to make a first simple version where the user can subscribe by clicking on a checkbox. This is what I get so far
<div class = 'wrapper'>
<%= render :partial => "my_account_leftbar" %>
<% form_for #subscription, :url => subscribe_user_path(current_user) do |f| %>
<div class="field">
<%= (f.check_box :type?) %> <!-- add '?'after the symbol, source: https://github.com/justinfrench/formtastic/issues/269 -->
</div>
<div class="actions">
<%= f.submit "Subscribe", :class => "button mr8" %>
</div>
<% end %>
</div>
Problems:
the app inserts a record into the db, but the attribute I defined in the form (type) has not been set (it should set '1' which stands for 'standard subscription') . How to get the app set this attribute?
how to set the FK? I assume that rails should set the FK automatically, is that assumption correct?
how to set the other values 'started_at' and 'ended_at? Those are datetime(timestamp) values...
Just run out of my beginner rails knowledge, any help really appreciated...
'Type' is a ruby on rails reserved word which should only be used when you are using Single Table Inheritance. You should rename your column name to something else.
I could solve the other questions 2 and 3 as well, wrapping it up:
insert the record: as stated in the answer from Wahaj, renaming the column 'type' into e.g. 'subscription_type' helped. I created a seperate migration as described here: How can I rename a database column in a Ruby on Rails migration?
storing the FK: updated the action in the controller. Instead of just writing
#subscription = Subscription.new(params[:subscription])
I wrote the following method to create a 'user's subscription'
#subscription = current_user.subscriptions.build(params[:subscription])
storing the 'started_at': added a method to the controller:
#subscription.update_attributes(:started_at => Time.zone.now)

Resources