ActiveAdmin default_actions cause route error - ruby-on-rails-3

I am trying to create a basic ActiveAdmin app to handle the content of a MySQL table. The view works fine, as does the New Store Group button and form. However, when I try to add default_actions for basic CRUD I get a routing error. Any idea why this happens?
My register code under app/admin/:
ActiveAdmin.register StoreGroup do
index do
column "Group ID", :GRP_ID
column "Group Name", :GRP_NM
column "Location Number", :LOC_NBR
end
filter :GRP_NM, :label => "Group Name"
filter :LOC_NBR, :label => "Location Number"
form do |f|
f.inputs "Store Group Details" do
f.input :GRP_ID, :label => "Group ID"
f.input :GRP_NM, :label => "Group Name"
f.input :LOC_NBR, :label => "Location Number"
end
f.actions
end
end
That much works fine. When I add default_actions under column "Location Number", :LOC_NBR I get for following error when I navigate to the page:
No route matches {:action=>"show", :controller=>"admin/store_groups", :id=>#<StoreGroup GRP_ID: 10, GRP_NM: "Damien", LOC_NBR: "99999">}
That is a valid object in my database, and displays just fine when default_actions is not present.
For reference, here is my model:
class StoreGroup < ActiveRecord::Base
establish_connection "zeus_#{Rails.env}"
set_table_name "str_grp"
# Setup accessible (or protected) attributes for your model
attr_accessible :GRP_ID, :GRP_NM, :LOC_NBR, :email
end
I am running Ruby version 1.9.3p385 and Rails 3.2.12.
Thank you.

Related

Memory (hex) address being returned instead of value in Rails

I'm definitely a bit of a noob, so this might be something simple that I'm overlooking, however, the searches that I've done to try and find a solution have come up empty.
I've built a form using formtastic that has 5 input fields: two are text boxes and three are select lists.
<%= semantic_form_for #player do |f| %>
<%= f.inputs do %>
<%= f.input :firstname, :label => "First Name " %>
<%= f.input :lastname, :label => "Last Name " %>
<%= f.input :leagueid, :as => :select, :collection => League.all(:order => :leaguename), :label => "League " %>
<%= f.input :team_1, :as => :select, :collection => Team.all(:order => :name), :label => "Team 1 " %>
<%= f.input :team_2, :as => :select, :collection => Team.all(:order => :name), :label => "Team 1 " %>
<% end %>
<%= f.actions %>
<% end %>
What is happening is that the Teams lists work perfectly (the team names are displayed). However, the League list is a different story. All of the entries in the list look like this (with different a different code after 'League:'):
#<League:0x007fe29c406498>
If I use the form to create a Player, it works fine. The correct league ID goes into the database and everything. I just can't figure out why the names of the teams show, while whatever-that-is shows for the league.
Any and all help is appreciated.
When converting objects to String, Ruby will convert them to the memory address like you see unless you provide a to_s method for string conversions. I haven't used formtastic, but I believe adding a to_s method to your League class should cause it to display what you want.
Try adding
def to_s
#name # use whatever you want to be displayed.
end
to the League class.
You could try explicitly specifying the fields that should be used as the text and id within the select list. I believe it would look like.
<%= f.input :leagueid, :as => :select, :collection => Hash[League.all.map { |league| [league.leaguename, league.id] }]
The syntax is crazy. The call to map is returning an array of name/id pairs, like [ ['league1', 1], ['league2', 2] ]. Calling Hash on that converts it to a hash, like {'league1' => 1, 'league2' => 2}. Seems like the select list should use this hash to populate itself.
There's an example of this at http://rdoc.info/github/justinfrench/formtastic, under the Usage section.
:member_name is the solution I think.
<%= f.input :leagueid, :as => :select, :collection => League.all(:order => :leaguename), :label => "League " %>
Will probably work for you as
<%= f.input :leagueid, :as => :select, :member_name => :league, :collection => League.all(:order => :leaguename), :label => "League " %>
My problem was I have a model with a field the same name as the model and I think that confused Formtastic
Example scaffold:
rails g scaffold Countries code:string country:string
rails g scaffold Types title:string description:string
Model:
class Sign < ActiveRecord::Base
attr_accessible :title, :country_id, :type_id
belongs_to :country
belongs_to :type
Form View:
<%= f.input :type %>
<%= f.input :country, :member_label => :country %>
Having a form without the member_label leads to the object id displaying in the select box for countries although the ID is correctly saved. The type select worked perfectly without declaring member_label.
Note that I didn't need to specify :as => :select as formtastic can deduce this from the belongs_to relationship.
Try using :
<%= form.input :league, :member_label => :leaguename %>
This will override the naming convention of the Formtastic Column Select.
Have a look also at : Overriding the Column Name Convention wiki
Hope this helped.

Rails: Uniqueness of two attributes in join table causing 500 error

I have the following models, which basically are trying to mean that a professor has knowledge of many subjects for a particular level. The subjects are fixed, so there will be no new subjects created, there will be just "related" to a professor through the knowledge join table.
class Subject < ActiveRecord::Base
# Self Associations
has_many :subcategories, :class_name => "Subject"
belongs_to :category, :class_name => "Subject",:foreign_key => "parent_id"
# Associations
has_many :knowledges
has_many :professors, :through => :knowledges
end
class Professor < ActiveRecord::Base
# Associations
has_many :knowledges
has_many :subjects, :through => :knowledges
...
end
class Knowledge < ActiveRecord::Base
# Associations
belongs_to :professor
belongs_to :subject
has_one :level
attr_accessible :subject_id, :professor_id
validates :subject_id, :uniqueness => { :scope => :professor_id }
end
I want to have a form that will let a professor to add a subject to his account, and I decided to have a form for a knowledge (as I want to be able to insert a level too).
It looks like this:
<%= simple_form_for #knowledge,:url => professor_knowledges_path, :html => { :class => 'form-horizontal' } do |f| %>
<div class="control-group select optional">
<%= label_tag "Subject Type", nil, :class => "select optional control-label"%>
<div class="controls">
<%= select_tag "Parent Subject", options_from_collection_for_select(#parent_subjects, "id", "name"), :id => "knowledge_parent_subject" %>
</div>
</div>
<%= f.input :subject_id, :collection => #subjects, :label => "Subject" %>
<%= f.input :level %>
<%= f.button :submit, t('add_form'),:class => 'btn-primary' %>
<% end %>
And in the create action of the Knowledges controller I have this:
def create
#knowledge = Knowledge.create(:professor_id => current_professor.id, :subject_id => params[:knowledge][:subject_id])
end
I would like/expect to get an ActiveRecord saying that this knowledge can't be inserted because there is a uniqueness violation, but nops, I just see a 500 in the logs and a rollback, but it seems the execution goes on. So my question is: What am I doing wrong, or how I could improve this modeling situation? I believe the form needs to be related to the join model as I want to have fields of that model on it...But maybe I am wrong, and I could do in an easy/cleaner way.
EDIT:
As asked in one of the comments, here is the log of the submission of the form and the 500 error right after the rollback:
Started POST "/professors/1/knowledges" for 127.0.0.1 at 2012-07-01 00:45:39 -0700
Processing by KnowledgesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4JVyxWnIh37kyBwLwLGTHk/znsI1c5wrJvaWjKKT5tM=", "Parent Subject"=>"1", "knowledge"=>{"subject_id"=>"1"}, "commit"=>"Añadir", "professor_id"=>"1"}
Professor Load (0.4ms) SELECT `professors`.* FROM `professors` WHERE `professors`.`id` = 1 LIMIT 1
Completed 500 Internal Server Error in 4ms
I added some conditions in the create action, like this:
def create
#knowledge = Knowledge.new(:professor_id => current_professor.id, :subject_id => params[:knowledge][:subject_id])
if #knowledge.save
flash[:notice] = "Success..."
redirect_to professor_path(current_professor)
else
render :action => 'new'
end
end
And this actually shows the following right after the 500:
Completed 500 Internal Server Error in 6ms
ActiveRecord::RecordInvalid (Validation failed: Subject has already been taken):
I wonder why the exception is raised instead of just adding the errors into the object and let me manage that situation. Isn't what the following line should be doing?
validates :subject_id, :uniqueness => { :scope => :professor_id }
That error means you are trying to insert duplicate subject_id / professor_id pairs on that table. Most often happens when either the subject_id or professor_id is null.
Are you sure the controller is getting the correct parameters? I would check the logs to make sure the inserts are what you would expect.
I don't have enough reputation to comment...my answer is more some things to try than a definitive answer, sorry.
It looks like the save is failing due to validation errors. You can try to handle those in your 'else' block. The following will give you a description of all validation errors (useful for debugging).
#knowledge.errors.full_messages
You haven't shown what is happening in the 'new' action. I suspect this is where the errors are occurring.
Does the same issue occur (i.e. the validation problem) in the console? If so, try cleaning out your databases (beware - the following will erase & rebuild all your databases).
rake db:drop:all db:create:all db:migrate db:test:prepare
Also, if you haven't already, add an index to your migration for Knowledge to prevent duplicates being added to the db. e.g.
add_index :knowledges, [ :professor_id, :subject_id ], unique: true

Formtastic / ActiveAdmin multi-select many to many checkbox association issue

I am using activeadmin and it has formtastic built in as many of you who use it know. I have a model called Project that has a many to many association with ProjectResources.
My custom "edit" and "creation" form in active admin for Project looks like so.
form do |f|
f.inputs "Project" do
f.input :name, :input_html => { :readonly => true }
end
f.inputs "Resources" do
f.input :id, :label => "Selected Resources",
:as => :check_boxes,
:multiple => true,
:collection => ProjectResource.all,
:selected => #resources
end
f.buttons
end
My checkboxes render just fine and I don't get any errors at this point. The problem if you may have guessed is that when rendering the "edit" page I would like to show items in the checkbox area as "selected" if the Project has a ProjectResource as an association already.
Right now the checkboxes all show a deselected state. I am using the latest version of activeadmin and formtastic has the following versions installed. (2.2.0, 2.1.1, 2.1.0, 2.0.2, 1.2.4)
Not sure what version activeadmin uses at this point. My guess is the latest version.
For me, simple:
ActiveAdmin.register Subscription do
form do |f|
f.inputs do
f.input :users, as: :check_boxes
# other fields...
end
f.buttons
end
end
just works.
More Code:
-User class
class User < ActiveRecord::Base
has_and_belongs_to_many :users
attr_accessible :fields...
end
-Subscription Class
class Subscription < ActiveRecord::Base
has_and_belongs_to_many :subscriptions
attr_accessible :fields...
end
PS I am using ActiveAdmin 0.4.2 and Formtastic 2.0.2.

mongoid save embedded documents

I'm trying to build up on the following tutorial from railscast:
http://railscasts.com/episodes/196-nested-model-form-part-1
I'm trying to make everything work with mongodb and mongoid.
the scenario is:
I want to creates events linked to a location. Each events (dance class) contains many lessons.
So I thought that an embedded relationship would be perfect.
Here are my models
model Lesson
class Lesson
include Mongoid::Document
include Mongoid::Slug
field :name, :type => String
embedded_in :event
slug :name
end
model Event
class Event
include Mongoid::Document
include Mongoid::Slug
include Mongoid::Timestamps
include Mongoid::MultiParameterAttributes
field :name, :type => String
field :description, :type => String
field :date, :type => DateTime
validates_presence_of :name
has_one :venue
referenced_in :venue
embeds_many :lessons
slug :name
end
model Venue
class Venue
include Mongoid::Document
include Mongoid::Slug
include Mongoid::Timestamps
include Mongoid::MultiParameterAttributes
field :name, :type => String
field :location, :type => String
validates_presence_of :name, :location
belongs_to :event
slug :name
end
event controller
def create
#event = Event.new(params[:event])
if #event.save
flash[:notice] = 'Event was successfully created.'
end
respond_with(#Event, :location => events_url)
end
def update
# #event = Event.find(params[:id])
#event = Event.find_by_slug(params[:id])
if #event.update_attributes(params[:event])
flash[:notice] = "Event was succesfully updated"
end
respond_with(#event)
end
Then I have my Event view where I can create events and link it to a Venue. But I'd like to be abe to create the lessons from the Event view/model.
so I used the fields_for to generate a field linked to the Lessons model.
= form_for #event do |f|
.field
= f.label :name
%br/
= f.text_field :name
.field
= f.label :description
%br/
= f.text_area :description
.field
= f.label :venue_id
%br/
= f.collection_select :venue_id, Venue.all, :id, :name
.field
= f.label :date
%br/
= f.datetime_select :date
%h3 Add a Class
= f.fields_for :lessons do |builder|
= render "lesson_fields", :f => builder
.actions
= f.submit 'Save'
When I create or edit a new event I get an error message:
undefined method `extract_id' for "test":String
But the request parameter message on the error page shows my lessons value in the Event document.
"lessons"=>{"name"=>"test name lesson"}
When I remove the fields_for line, everything works fine. But then i don't know how to save the value for the nested documents.
I have same problem with embeds_many, but when i try change to has_many. It works!. Maybe you can try too.
can you post the exact code you use to create the Event, including parameters?
which version of Mongoid and Rails are you using?
First thing I noticed is that the following parameter hash does not match your Lessons model:
"lessons"=>{"content"=>"test name lesson"} # this looks wrong
this should be:
"lessons"=>{"name" => "test name lesson"}
Looks like your lessons form has the wrong label for the text input field .. it should be :name , not :content
To dry things up, you might want to try if the 'nested_form' gem works for you:
after installing the gem, use the nested_form_for instead of form_for in your view.
Check here for a more detailed description:
How can I handle this type of multi level forms in rails
See:
https://github.com/ryanb/nested_form (it's also referenced in the RailsCast you mentioned)
You also might want to check this:
field_for and nested form with mongoid
The conclusion of this story is...
I removed everything related to mongoid_slug and it started to work.
I then put everything back as it was to try to find out how to make it work with mongoid_slug and it just worked, like out of the box.
:(
Please include the following code in model event.rb
**accepts_nested_attributes_for :lessons**
This will fix your problem

Validation on a Complex Model for a multi-page form

I'm trying to write a registration using devise and active merchant. The form is complex in that my user object looks like this:
class User < ActiveRecord::Base
include ActiveMerchant::Utils
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
# Setup accessible (or protected) attributes
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :first_name,
:subscription_attributes, :last_name, :zipcode,
:payment_profile_attributes, :customer_cim_id, :payment_profile_id
...
# Track multi-page registration
attr_writer :current_step
...
# Setup Payment Profile element (authorize.net billing profile)
has_one :payment_profile, :dependent => :delete
accepts_nested_attributes_for :payment_profile
Now the PaymentProfile class has its own children, two items from active merchant:
require 'active_merchant'
class PaymentProfile < ActiveRecord::Base
include ActiveMerchant::Billing
include ActiveMerchant::Utils
validate_on_create :validate_card, :validate_address
attr_accessor :credit_card, :address
belongs_to :user
validates_presence_of :address, :credit_card
def validate_address
unless address.valid?
address.errors.each do |error|
errors.add( :base, error )
end
end
end
def address
#address ||= ActiveMerchant::Billing::Address.new(
:name => last_name,
:address1 => address1,
:city => city,
:state => state,
:zip => zipcode,
:country => country,
:phone => phone
)
end
def validate_card
unless credit_card.valid?
credit_card.errors.full_messages.each do |message|
errors.add( :base, message )
end
end
end
def credit_card
#credit_card ||= ActiveMerchant::Billing::CreditCard.new(
:type => card_type,
:number => card_number,
:verification_value => verification_code,
:first_name => first_name,
:last_name => last_name
)
#credit_card.month ||= card_expire_on.month unless card_expire_on.nil?
#credit_card.year ||= card_expire_on.year unless card_expire_on.nil?
return #credit_card
end
Now I've overrided the RegistrationsController from Devise to handle the multi-page form using the solution from Ryan Bates multi-page form screencast (http://railscasts.com/episodes/217-multistep-forms). I had to tweak it a bit to get it working with Devise, but I was successful. Now because Ryan's multi-page form simply asked for different fields from the same model on different pages, he was able to override his valid? method by adding an :if block to his validate method a la:
validates_presence_of :username, :if => lambda { |o| o.current_step == "account" }
But in my case, I'm asking for all the fields on the first form from my parent model (User), and then asking for the all the fields from my two grandchild models (User:PaymentProfile:Address, User:PaymentProfile:Credit_Card) on teh second page.
The problem I'm facing is that although PaymentProfile.valid? returns errors based on ActiveMerchant's logic, the form itself doesn't render or even display those errors. The view code for the billing page looks like this:
<h2>Payment Details</h2>
<%= semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.semantic_fields_for :payment_profile do |p| %>
<%= p.semantic_fields_for :address do |a| %>
<%= a.inputs "Billing Information", :id => "billing" do %>
<%= a.input :type, :label => "Credit Card", :as => :select, :collection => get_creditcards %>
<%= a.input :number, :label => "Card Number", :as => :numeric %>
<%= a.input :card_expire_on, :as => :date, :discard_day => true, :start_year => Date.today.year, :end_year => (Date.today.year+10), :add_month_numbers => true %>
<%= a.input :first_name %>
<%= a.input :last_name %>
<%= a.input :verification_code, :label => "CVV Code" %>
<% end %>
<% end %>
<%= f.semantic_fields_for :credit_card do |c| %>
<%= c.inputs "Billing Address", :id => "address" do %>
<%= c.input :address1, :label => "Address" %>
<%= c.input :city %>
<%= c.input :state, :as => :select, :collection => Carmen::states %>
<%= c.input :country, :as => :select, :collection => Carmen::countries, :selected => 'US' %>
<%= c.input :zipcode, :label => "Postal Code" %>
<%= c.input :phone, :as => :phone %>
<% end %>
<% end %>
<% end %>
<%= f.commit_button :label => "Continue" %>
<% unless #user.first_step? %>
<%= f.commit_button :label => "Back", :button_html => { :name => "back_button" } %>
<% end %>
<% end %>
I added a puts errors message in my code right after the valid? command and it shows as follows:
{:base=>[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]}
{:base=>[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]}
Now the structure of this output doesn't match the output of a standard error output which is built off a single layer hash such as:
{:username=>["can't be blank"]}
So after showing you all of that, my questions are these:
a) how do I get the error output to show properly so that the form actually spits them out?
b) how do I prevent the parent.valid? from also validating the grandchildren.valid? when I'm not on that page? I can't use the :if => lambda... solution on child models because they don't know what the current_step is.
My apologies for such a long post, I just wanted to include as much information as possible. I've been wrestling with this for a week now and I can't seem to get past it. Any advice would be hugely helpful. Thanks in advance.
The reason the errors aren't showing is probably that they are populated on the base, not on the individual attributes. This happens in your validate_card and validate_address methods. Instead of adding errors to base, you should add them to the specific attribute that caused the error.
errors.add( attr , error )
Secondly, if you want to make your validations dependent on a certain state, as the screencast you mentioned, then you need to save the state flag with the model (probably best the parent). You can do this by hand or, better, you can use a gem for this (recommended): state_machine
Good luck.
On a high level, you seem to be using inheritance in your object modeling and this model is getting built in several forms, in almost 'wizard' like approach. My suggestion would be to model your objects to reflect, the actual forms like,
First part of the form collect basic User information : UserInformation model
Second Part of the form collect payment related information: PaymentInformation model (the Active merchant stuff)
and so on...
Where either the User model has one UserInformation, has one PaymentInformation and so on.
Essentially replace inheritance with Composition. Try and see if you can avoid extending the ActiveMerchant frame work too.
The above style, will give you more control over when you want to call #valid? on a subset of you data model. It get constructed part by part as the user move through the form.
Sorry, I dont have specific solution for you but a more general rewrite approach.
I am new to Ruby-on-Rails and I know this doesn't answer the questions above but you should try out Client-Side Validations and take a look at the Rails-casts. It may be helpful to you!