I have a model Camping which has_many Images. At least one image is required on Camping:
class Camping < ActiveRecord::Base
attr_accessible :images_attributes
has_many :images
validates_presence_of :images, :message => "At least one image is required"
accepts_nested_attributes_for :images, :allow_destroy => true
end
Then, in active_admin, which uses formtastic, I render the error message At least one image is required, with f.semantic_errors:
ActiveAdmin.register Camping do
form :html => { :multipart => true } do |f|
f.semantic_errors :images
#....
f.inputs "Images" do
f.has_many :images do |img|
#....
end
end
#....
end
end
This renders as:
Images At least one image is required.
How can I make it render: At least one image is required?
changing the f.semantic_errors :images into 'f.semantic_errors (removing :images) makes it render nothing; no error at all.
Note: The API documentation seems to imply that Formtastic always adds the :attribute name to the error; but I am not entirely sure how this code works.
If you want to use such custom messages you can add error messages that are related to the object’s state as a whole, instead of being related to a specific attribute
Change this
validates_presence_of :images, :message => "At least one image is required"
to something like
validate :should_have_images
def should_have_images
errors.add(:base, "At least one image is required") if images.blank?
end
If you want to use such custom messages you can add new method to Formtastic::Helpers::ErrorsHelper As follows
create new file at config/initializers/errors_helper.rb
Place following code to file
module Formtastic
module Helpers
module ErrorsHelper
def custom_errors(*args)
return nil if #object.errors.blank?
messages = #object.errors.messages.values.flatten.reject(&:blank?)
html_options = args.extract_options!
html_options[:class] ||= 'errors'
template.content_tag(:ul, html_options) do
messages.map do |message|
template.content_tag(:li, message)
end.join.html_safe
end
end
end
end
end
In activeadmin form use
f.custom_errors instead of f.semantic_errors *f.object.errors.keys
Related
I am using ActiveAdmin on my Rails project. I use Draper as decorator but I don't understand why the show view is not decorated. According to the documentation it should be working just adding the decorate_with MyDecorator in my ActiveAdmin resource.
Here is my code:
ActiveAdmin.register Home do
...
decorate_with HomeDecorator
show do
attributes_table do # not being decorated
row :content
row :status
row :image
row :author_with_avatar
end
end
end
Does someone know what is wrong with my code ?
When I've run into this problem, it's because I've customized the controller's find_resource method. In that case be sure to return a manually decorated object:
def find_resource
MyModel.find_by_some_unique_finder(params[:id]).decorate
end
If all else fails you can force it using this approach:
show
attributes_table_for model.decorate do
row :decorator_method
row(:custom_label) { |m| m.decorator_method }
See http://activeadmin.info/docs/6-show-pages.html for more details
Research: Rails 4.0 beta, fields_for not accepting pluralized model_name in one-to-many association, Rails 4 Nested Attributes with fields_for Don't Save to Database
First, let's get the most common problem out of the way: incorrectly named attributes parameters for strong parameters. Mine is correctly plural.
class AdultsController < ApplicationController
...
def update
authorize #user
respond_to do |format|
if #user.update_attributes(user_params)
format.html { redirect_to unit_adult_path(#unit, #user), notice: "#{#user.full_name} was successfully updated." }
else
format.html { render action: 'edit' }
end
end
end
def user_params
params.require(:adult).permit(:first_name, :last_name, phones_attributes: [])
end
end
And my models are setup correctly
class User < ActiveRecord::Base
has_many :phones, dependent: :destroy
accepts_nested_attributes_for :phones, allow_destroy: true, reject_if: proc { |a| a["number"].blank? }
end
class Phone < ActiveRecord::Base
belongs_to :user, touch: true
end
And the view
# adult/_form.html.haml
= bootstrap_form_for [#unit, #user] do |f|
= f.text_field :first_name, control_col: 'col-md-4'
= f.text_field :last_name, control_col: 'col-md-4'
= f.fields_for :phones do |f_phone|
= f_phone.form_group do
= f_phone.select :kind, options_for_phones, hide_label: true, layout: :default
= f_phone.phone_field :number, hide_label: true, layout: :default
= f_phone.check_box :_destroy, label: 'remove'
But, when I submit the User form to save
Started PATCH "/units/2/adults/1" for 127.0.0.1 at 2014-07-11 15:20:17 -0700
Processing by AdultsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"pDjDVSiEs5qqHLqnbxQMeGWDOUGvhXPPvgyRGmitmps=", "adult"=>{"first_name"=>"Karl", "last_name"=>"Smith", "phones_attributes"=>{"0"=>{"kind"=>"other", "number"=>"888.1212", "_destroy"=>"0", "id"=>"173"}, "1"=>{"kind"=>"mobile", "number"=>"888.1212", "_destroy"=>"0", "id"=>"174"}} }, "commit"=>"Update Adult", "unit_id"=>"2", "id"=>"1"}
Unpermitted parameters: phones_attributes
I don't understand why the nested data is being rejected by the strong parameter evaluation. It looks correct to me.
The one thing I do notice is that the params data for "phones_attributes" value is a HASH not an ARRAY. In the user_params, phones_attributes: [] looks like it expecting an ARRAY. So I changed it to a HASH.
def user_params
params.require(:adult).permit(:first_name, :last_name, phones_attributes: {})
end
But now I get the following error.
Unpermitted parameters: 0, 1
So I tried specifying the field names in the "phones_attributes" array.
def user_params
params.require(:adult).permit(:first_name, :last_name, phones_attributes: [:id, :kind, :number])
end
And I still get.
Unpermitted parameters: phones_attributes
I know I must be missing something small, but I can't find my error.
EDIT: all my nested attributes forms do not work. Not 100% sure when they stopped, but ones that worked previously no longer work and have not been modified.
Figured this out. I was using javascript to copy the phone fields (kind, number) to make a new set of inputs available for entry. The script was adding non numeric characters to part of the field id, and this was causing rails to ignore all the submitted phone_attributes.
For the next person that comes along...
When fields_for renders out the fields, it will index each input name for uniqueness when the submitted post data is converted to params. In the example below, this number field
<input id="adult_phones_attributes_0_number" name="adult[phones_attributes][0][number]" type="tel" value="7773331111">
will look something like this when converted to params
"phones_attributes"=>{"0"=>{"number"=>"7773331111"}}
The hash key of "0" comes from the index created by fields_for. It's the "[0]" portion of the name.
In versions of rails past, if that nested attributes params hash key was not a number, the k/v pair was just ignored. Well now with strong parameters (I'm guessing the culprit), it will reject the entire "phones_attributes" hash.
My script was copying the input field, doing a regex on the html to change the "[0]" index to a random number. But sometimes it would replace it will non-digit characters. And this was causing the problem.
I am having problem to use page object method call to return an object on the page.
Here is my example codes in test_log_in.rb
...
class TestLogIn < Test::Unit::TestCase
.....
def test_failed_log_in
#log_in_page= LogIn.new(#browser)
#log_in_page.go_to_log_in
#log_in_page.log_in("174773476","test","aaa111as")
puts #log_in_page.error_message
puts #log_in_page.get_error_message
end
end
My log in class is defined below:
class LogIn
include PageObject
...
h3(:error_message, :class => 'no-margin white-text')
...
def log_in (access_number, user_id, password)
self.access_number = access_number
self.user_id = user_id
self.password = password
log_me_in
AccountSummary.new(#browser)
end
....
def get_error_message
self.error_message
end
....
end
Why will the following lines returns no output?
puts #log_in_page.error_message
puts #log_in_page.get_error_message
Can you please help me?
Thanks.
My guess is that there is a timing issue due to the error message being displayed through javascript. It is likely that the error message element is always in the html of the page, but when login fails, the error message element's style is changed to be something visible.
When a page object (or watir) element returns its text, it only includes the visible text. So if the error message is not yet visible, you get no text.
Try waiting to ensure the element is visible to the user:
def get_error_message
error_message_element.when_present.text
end
when_present will wait up to 5 seconds for the element to become visible to the user and then return the text.
Update - Multiple h3s:
It looks like the actual problem is that there are multiple h3 elements that have that class. Watir always returns the first match, which in this case is the cookie error heading. Given that it is not being displayed, you get the blank text.
The solution would be to reduce the search scope so that you the first h3 is the one you want. This can be done by only looking in the error message list. I would define the page object as follows. Note that you do not need to specify the class of the h3 element since it is the only h3 element in the list.
class LogIn
include PageObject
div(:error_message_list, :id => 'errorMessageList')
h3(:error_message){ error_message_list_element.h3_element }
end
This will then find the right h3 element:
browser = Watir::Browser.new
browser.goto 'https://www.bnz.co.nz/ib4b/app/login'
browser.button(:text => 'Login').click
page = LogIn.new(browser)
page.error_message
#=> "There were 3 errors on the page."
I have 2 models. Category and Post. They are connected using a has_many_and_belongs_to_many relationship. I checked in the rails console and the relationship works.
I created checkboxes in activeadmin to set the post categories using this form field:
f.input :categories, as: :check_boxes, collection: Category.all
The problem is when I try to save it because every other field data (title, body, meta infos etc.) is saved, but the category stays the same even if I unchecked it, or checked another too.
I am using strong parameters like this:
post_params = params.require(:post).permit(:title,:body,:meta_keywords,:meta_description,:excerpt,:image,:categories)
Please give me some suggestions to make active admin save the categories too!
Best Wishes,
Matt
Try this in AA:
controller do
def permitted_params
params.permit post: [:title, :body, :meta_keywords, :meta_description, :excerpt, :image, category_ids: []]
end
end
Put something like this in /app/admin/post.rb:
ActiveAdmin.register Post do
permit_params :title, :body, :meta_keywords, :meta_description, :excerpt, :image, category_ids: [:id]
end
If you are using accepts_nested_attributes_for then it would look like this:
ActiveAdmin.register Post do
permit_params :title, :body, :meta_keywords, :meta_description, :excerpt, :image, categories_attributes: [:id]
end
I've tested, this might works for you and others as well
# This is to show you the form field section
form do |f|
f.inputs "Basic Information" do
f.input :categories, :multiple => true, as: :check_boxes, :collection => Category.all
end
f.actions
end
# This is the place to write the controller and you don't need to add any path in routes.rb
controller do
def update
post = Post.find(params[:id])
post.categories.delete_all
categories = params[:post][:category_ids]
categories.shift
categories.each do |category_id|
post.categories << Category.find(category_id.to_i)
end
redirect_to resource_path(post)
end
end
Remember to permit the attributes if you're using strong parameters as well (see zarazan answer above :D)
References taken from http://rails.hasbrains.org/questions/369
I am creating a web application with Ruby on Rails 3.1 (RC1). I am using Factory Girl, RSpec and Cucumber (with Capybara) for testing, but I am experiencing unexpected raised ActionDispatch::ClosedErrors some of the times (not every time) when I am creating new users (through the User model's create action). Below is the error message that I get:
Cannot modify cookies because it was closed. This means it was already streamed
back to the client or converted to HTTP headers. (ActionDispatch::ClosedError)
The error is raised when using these ways of creating users:
Creation using Factory Girl
Factory.create( :user )
Factory.build( :user ).save
Basic creation
User.create( { ... } )
User.new( { ... } ).save
What is funny is that they do work during some test, but not in others, and it does not seem random, although I cannot figure out the reason. Below is an excerpt from my code:
users_controller_spec.rb
require 'spec_helper'
def user
#user ||= Factory.create( :user )
end
def valid_attributes
Factory.attributes_for :user
end
describe UsersController do
describe 'GET index' do
it 'assigns all users as #users' do
users = [ user ] # The call to user() raises the error here
get :index
assigns[ :users ].should == users
end
end
describe 'GET show' do
it 'assigns the requested user as #user' do
get :show, id: user.id # The call to user() raises the error here
assigns[ :user ].should == user
end
end
However, the error is not raised in the following code block:
describe 'GET edit' do
it 'assigns the requested user as #user' do
get :edit, id: user.id # This raises no error
assigns[ :user ].should == user
end
end
Any other method below this does not raise the error, even though I am creating users in the exact same way.
Any suggestions to what I might be doing wrong would be greatly appreciated!
Someone posted a workaround here
https://github.com/binarylogic/authlogic/issues/262#issuecomment-1804988
This is due to the way rails 3 streams the response now. They posted a fix in edge for the same issue in flash but not in cookies yet. For now I have turned off my request specs. I am going to look at the problem this weekend if no one gets to it before then.
https://github.com/rails/rails/issues/1452
Just so we don't have to follow links, here's my modified version of the authlogic workaround:
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.maintain_sessions = false if Rails.env == "test"
end
end
Rather than deal with ensuring session management on every .save call, I just turn them off if I'm testing.