Reserved word issue with ActiveAdmin rails 3.2 - ruby-on-rails-3

I have made the following addition to my active admin interface:
action_item :only => :show do
link_to('Approve this article', approve_admin_article_path(article)) if article.approved.nil?
end
member_action :approve, :method => :get do
# do approval
redirect_to :action => :show, :notice => "Approved!"
end
This throws the following error:
undefined method `approved' for
:Arbre::HTML::Article
What I think is happening is Active Admin thinks I'm passing an article tag in, not an article class?
Does anyone know of a work around for this? perhaps aliasing?
Thanks!
class Article < ActiveRecord::Base
attr_accessible :body
# Relations:
belongs_to :articleable, polymorphic: true, :counter_cache => true
has_many :comments, as: :commentable, order: 'created_at DESC', dependent: :destroy
# Validations
validates_presence_of :body
validates_length_of :body, maximum: 15000
end

Found a workaround
There is something fishy when you name your class as 'Article', ActiveAdmin relate to it when rendering as <article> HTML tag - The problem is somewhere in the controller of course because this is where the article object is being generated
So, I override the controller
ActiveAdmin.register Article do
controller do
def show
# grabbing my desired Article and not the <article> tag into some global variable
##myarticle = Article.find(params[:id])
end
end
sidebar :article_details , :only => :show do
ul do
# using the ##myarticle which I know should be initialized
# (you can put .nil? checking here if you want)
li link_to 'Article Images (' + ##myarticle.images.count.to_s + ')' , admin_article_article_images_path(##myarticle)
li link_to 'Article Clips ('+##myarticle.clips.count.to_s + ')' , admin_article_article_clips_path(##myarticle)
end
end
end
Enjoy

Assuming you're having the issue in the 'show' block, you could change the show block to the following:
show do |object|
end
Then you can call object.some_method without the clash. This way you don't need to override the controller.

Related

Use member in rails

Hi everyone I am new with rails 3,I have a app where I want to associate a idea with a comment.
When I show an idea, in the bottom of the view I show a form to put new comment for this idea, and when a click to save a comment, I have to pass the idea_id, I create my model commment
belongs_to :user
belongs_to :idea
attr_accessible :description, :likes, :name, :user_id, :idea_id
in the view of show idea a put this
= render :partial => "comments/index", :collection => #idea.comments
= render :partial => "comments/form", :locals => {:comment=> #comment}
in the _form of the comment I include idea to obtain idea_id to save
= form_for [#idea, #comment] do |f|
and in my router I put this
resources :ideas do
member do
resources :comments
end
end
and now I obtain this error
undefined method `idea_comments_path'
any idea, anyone knows a document to explain better how to use member in rails!
You don't need member for nested resources:
resources :ideas do
resources :comments
end
http://guides.rubyonrails.org/routing.html

Can't mass-assign protected attributes for nested form using cocoon and simple_forms in Rails 3 App

The Problem:
I am getting an error message when submitting my form that says:
ActiveModel::MassAssignmentSecurity::Error in AdmissionRecordsController#create
Can't mass-assign protected attributes: admission_record
My Setup:
I am using Rails 3.2.3, with extra gems including Cocoon 1.0.14 and Simple_Form 2.0.2
The View:
My app/views/admission_records/_form.html.haml looks like:
= simple_form_for [#admission, #record] do |f|
= f.simple_fields_for :vital_signs, #record.vital_signs.build do |vs|
= render :partial => "vital_sign_fields", :locals => { :f => vs }
= link_to_add_association "Add Vital Signs", f, :vital_signs
= f.submit
And my app/views/admission_records/_vital_sign_fields.html.haml looks like:
.nested-fields
= f.label :sbp
= f.text_field :sbp
...
= link_to_remove_association "Remove Vital Sign"
What I am basically trying to do is that I have a resource called AdmissionRecord nested within another resource called PatientAdmission (route.rb shown below). I have another resource called VitalSign which I want to be able to create via a nested form (using cocoon and simple_form) when creating the AdmissionRecord
My config/routes.rb file looks like:
resources :patient_admissions do
resources :admission_records
end
The Models:
My app/models/patient_admission.rb looks like:
class PatientAdmission < ActiveRecord::Base
has_many :admission_records, :dependent => :destroy
end
My app/models/admission_record.rb looks like:
class AdmissionRecord < ActiveRecord::Base
belongs_to :patient_admission
has_many :vital_signs, :dependent => :destroy
accepts_nested_attributes_for :vital_signs, :rejects_if => :all_blank, :allow_destroy => true
attr_accessible :vital_signs_attributes
end
And my app/models/vital_sign.rb looks like:
class VitalSign < ActiveRecord::Base
belongs_to :admission_record
attr_accessible # just fields that appear in the form
end
The Controller:
The new and create methods in my AdmissionRecordsController looks like:
before_filter do
#admission = PatientAdmission.find(params[:patient_admission_id])
end
def new
#record = #admission.admission_records.build
end
def create
#record = #admission.admission_records.build(params[:admission_record])
#vital_sign = #record.vital_signs.build(params[:vital_signs])
#vital_sign.save
if #record.save
# Flash success and redirect to the right place
else
# Flash error and render :new
end
end
The Plea:
Please help me find where I'm going wrong. I've googled for hours and have looked at other examples and source code for demo apps such as those found in cocoon_simple_form_demo, but still can't seem to fix this error. If there's any other piece of information needed to debug this problem, please let me know. Thanks!
Okay I just had this problem and fixed it by entering one line of code in the belongs_to model.
# patient_admission.rb
Class PatientAdmission < ActiveRecord::Base
attr_accessible :admission_record_attributes
accepts_nested_attributes_for :admission_record
...
end
Here is another solution to it :)

Rails 3 current user id to comments?

So Im working on a rails app where users can comment on photos or videos another user has uploaded and so far everything is great except I am not able to get the current user_id associated with the person who has commented on the post. This is what I have so far.
user.rb
has_many :comments, :dependent => :destroy
photo.rb
has_many :comments, :as => :commentable
video.rb
has_many :comments, :as => :commentable
comments_controller.rb
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
redirect_to :id => nil, :notice => "Successfully created comment."
else
render :action => 'new'
end
end
How can I get the user id to appear with the current comments? I have the comment type and comment id I am just looking for a way to have it so the user_id can appear. Any suggestions?
You should add a hidden_field to your form partial where you store the current_user.id
something like:
<%= f.hidden_field :user_id, :value => current_user.id %>
of course you should have a field user_id in your comment model, as a comment belongs_to user and a user has_many comments.
update:
what ofca pointed out, this can approach can lead to security issues as the hidden field could be modified by the user in the browser, e.g. using firebug.
In this case it is probably better to to leave out this field in the view and create the comment in the controller by using
<%= current_user.comments.create(params[:comment]) %>
The way you have it now, it is only set up one way.
Plus you have to make it polymorphic
try adding:
comment.rb
belongs_to :user
belongs_to :commentable, :polymorphic => true

Suppress "base" in error text for custom validation of Rails nested attributes

I have the following models:
class Evaluation < ActiveRecord::Base
attr_accessible :product_id, :description, :evaluation_institutions_attributes
has_many :evaluation_institutions, :dependent => :destroy
accepts_nested_attributes_for :evaluation_institutions, :reject_if => lambda { |a| a[:token].blank? }, :allow_destroy => true
validate :requires_at_least_one_institution
private
def requires_at_least_one_institution
if evaluation_institution_ids.nil? || evaluation_institution_ids.length == 0
errors.add_to_base("Please select at least one institution")
end
end
end
class EvaluationInstitution < ActiveRecord::Base
attr_accessible :evaluation_institution_departments_attributes, :institution_id
belongs_to :evaluation
has_many :evaluation_institution_departments, :dependent => :destroy
accepts_nested_attributes_for :evaluation_institution_departments, :reject_if => lambda { |a| a[:department_id].blank? }, :allow_destroy => true
validate :requires_at_least_one_department
private
def requires_at_least_one_department
if evaluation_institution_departments.nil? || evaluation_institution_departments.length == 0
errors.add_to_base("Please select at least one department")
end
end
end
class EvaluationInstitutionDepartment < ActiveRecord::Base
belongs_to :evaluation_institution
belongs_to :department
end
I have a form for Evaluation that includes nested attributes for EvaluationInstitution and EvaluationInstitutionDepartment, so my form is nested to 3 levels. The 3rd level is giving me a problem.
The errors are triggered as expected, but when the error triggers for requires_at_least_one_department, the text reads
Evaluation institutions base Please
select at least one department
The message should read "Please select at least one department".
How do I remove "Evaluation institutions base"?
In Rails 3.2, if you take a look at the implementation of method full_message, you will see that it displays error messages through I18n with format "%{attribute} %{message}".
It means that you can customize the displayed format in your I18n locales as follows:
activerecord:
attributes:
evaluation_institutions:
base: ''
That would get rid of the prefix "Evaluation institutions base" as you wanted.
If anyone is looking for a solution for this that doesn't involve monkey patching, here's what I did in my errors partial. I simply look for "base" in the name of the attribute with the error and if it exists, I only post the message, otherwise I build the full_message. Now this won't work if you have attributes that have base in the name, but I don't so this works for me. It's a little hacky but so are the other solutions to this issue.
<% if object.errors.any? %>
<div id="error-explanation">
<div class="alert alert-error">
<ul>
<% object.errors.each do |atr, msg| %>
<li>
<% if atr.to_s.include? "base" %>
<%= msg %>
<% else %>
<%= object.errors.full_message(atr, msg) %>
<% end %>
</li>
<% end %>
</ul>
</div>
</div>
<% end %>
Adding the following monkey patch to initializers did the job for me in 3.2.3 with dynamic_form:
class ActiveModel::Errors
#exact copy of dynamic_form full_messages except 'attr_name = attr_name.sub(' base', ':')'
def full_messages
full_messages = []
each do |attribute, messages|
messages = Array.wrap(messages)
next if messages.empty?
if attribute == :base
messages.each {|m| full_messages << m }
else
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = #base.class.human_attribute_name(attribute, :default => attr_name)
attr_name = attr_name.sub(' base', ':')
options = { :default => "%{attribute} %{message}", :attribute => attr_name }
messages.each do |m|
if m =~ /^\^/
options[:default] = "%{message}"
full_messages << I18n.t(:"errors.dynamic_format", options.merge(:message => m[1..-1]))
elsif m.is_a? Proc
options[:default] = "%{message}"
full_messages << I18n.t(:"errors.dynamic_format", options.merge(:message => m.call(#base)))
else
full_messages << I18n.t(:"errors.format", options.merge(:message => m))
end
end
end
end
full_messages
end
end
If you aren't using dynamic_form, try the following instead (unless your form gem overrides errors.full_messages like dynamic_form does):
class ActiveModel::Errors
#exact copy of Rails 3.2.3 full_message except 'attr_name = attr_name.sub(' base', ':')'
def full_message(attribute, message)
return message if attribute == :base
attr_name = attribute.to_s.gsub('.', '_').humanize
attr_name = #base.class.human_attribute_name(attribute, :default => attr_name)
attr_name = attr_name.sub(' base', ':')
I18n.t(:"errors.format", {
:default => "%{attribute} %{message}",
:attribute => attr_name,
:message => message
})
end
end
The only change to original code is the following line:
attr_name = attr_name.sub(' base', ':')
Suggestions welcome.
This is an older question, but this issue just bit me again in Rails 6, so posting my solution here, since this is the most relevant SO post that covered the issue.
Example: Saving a top level class: 'Parent' containing a collection of 'Child', where 'Child' has a custom validation method:
e.g.
class Parent < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children, allow_destroy: true, reject_if: :all_blank
end
class Child < ActiveRecord::Base
belongs_to :parent
validate :custom_method
def custom_method
errors.add(:base, :error_symbol)
end
end
The following is needed:
Providing the locale entry for 'error_symbol'
Preventing the error for children rendering as 'Child Base ...'
Solution
First, add config.active_model.i18n_customize_full_message = true to your application.rb file.
Then, the following locale file works to override both the message and prevent 'Base' being prepended to the collection.
# config/locales/en.yml
en:
activerecord:
errors:
models:
parent/children:
format: 'Child: %{message}'
child:
error_symbol: "Error message goes here"
Interestingly, there seems to be some interaction here with accepts_nested_attributes_for, as I only was able to reproduce this issue when creating the parent and children with a single params object.
Should this not work for you, or you have a more complex issue, taking a look in ActiveModel/lib/active_model/errors.rb at the full_message method.
This should tell you whether:
The class in question is correctly picking up the i18n formatting for that class
What keys it is using to look in the locale files.

Embedded Document not being added

Having trouble adding an embedded document. I am trying to add a tag which is embedded in the user.
user.rb
class User
include Mongoid::Document
field :name
validates_presence_of :name
validates_uniqueness_of :name, :email, :case_sensitive => false
attr_accessible :name, :email, :password, :password_confirmation
embeds_many :tags
embeds_many :tasks
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
tag.rb
class Tag
include Mongoid::Document
field :name
embedded_in :user, :inverse_of => :tags
references_many :tasks
end
tags_controller.rb
def create
##user = User.find(:first, :conditions => {:_id => "4d3ae09bf5c4930b2b000004"} )
#user = current_user
#tag = Tag.new(params[:tag])
#user.tags << #tag
#tag.save
redirect_to #tag, :notice => "Tag created!"
end
This is the output to the server when I try to create a new tag.
Started POST "/tags" for 127.0.0.1 at 2011-02-18 13:46:03 -0500
Processing by TagsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"6p+Jova7Hol2v5LRReSp2fhNJ967EwkeIzAWyrChQRE=", "tag"=>{"name"=>"general"}, "commit"=>"Create Tag"}
db['users'].find({:_id=>BSON::ObjectId('4d39cd63f5c4930708000001')}, {}).limit(-1) MONGODB
db['users'].update({"_id"=>BSON::ObjectId('4d39cd63f5c4930708000001')}, {"$push"=>{"tags"=>{"name"=>"general", "_id"=>BSON::ObjectId('4d5ebe6bf5c493554d000002')}}}) Redirected to
http://localhost:3000/tags/4d5ebe6bf5c493554d000002 Completed 302 Found in 5ms
Not really sure what the issue is or where to start. It actually looks like the user is found then an update is being made to tags but it is not successful.
Thanks
The Tags class in your model is embedded inside of user (via the embeds_many association), rather than a table on its own. So following the updates in your controller, you should have something like this:
> db.users.find()
{
_id: ObjectId('4d39cd63f5c4930708000001'),
tags: [
{
_id: ObjectId('4d5ebe6bf5c493554d000002'),
name: "General"
}
]
}
Using MongoID, you can also have Tags appear in their own collection by replacing "embeds_many" with "references_many".
In the comments above, you'll see that the issue berek-bryan was having had to do with where the tag was being added. He expected the tag to be added in its own collection, hence the question. Actually, the tags were being added right into his users collection.