ruby on rails uninitialized constant error nested resources - ruby-on-rails

I'm new to Ruby on Rails and have not programmed for many years. I'm trying some simple code that is similar to the sample provided in the "Getting Started with Rails" guide for Rails 3.2. Instead of Posts and Comments my models are States and Counties. I've reviewed the code for problems with pluralization, but don't see anything out of place. My system is configured with Rails 4.0 and ruby 1.9.3. I encounter the error after I list the states from the index page. Once the states are listed, I select to show a state, which should allow me to add a County, but instead I get the following error on the page:
uninitialized constant State::County
The code listed with the error is from /app/views/states/show.html.erb
</p>
<h2>Add a County:</h2>
<%= form_for([#state, #state.counties.build]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
I'm providing other MVC files and the DB schema below.
Models:
state.rb
class State < ActiveRecord::Base
attr_accessible :abbr, :name
validates :abbr, :presence => true,
:length => { :maximum => 2 },
:format => { :with => /\A[A-Z]+\z/,
:message => "only 2 uppercase letters allowed" }
validates :name, :presence => true
has_many :counties, :dependent => :destroy
end
county.rb
class County < ActiveRecord::Base
attr_accessible :name, state_id
validates :name, :presence => true
belongs_to :state
end
Views
State/show.html.erb
<p id="notice"><%= notice %></p>
<p>
<strong>Abbr:</strong>
<%= #state.abbr %>
</p>
<p>
<strong>Name:</strong>
<%= #state.name %>
</p>
<h2>Add a County:</h2>
<%= form_for([#state, #state.counties.build]) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<br />
<%= link_to 'Edit', edit_state_path(#state) %> |
<%= link_to 'Back', states_path %>
Routes.rb
resources :states do
resources :counties
end
Controllers
counties_controller.rb
class CountiesController < ApplicationController
def create
#state = State.find(params[:state_id])
#county = #state.counties.create(params[:county])
redirect_to state_path(#state)
end
def destroy
#state = State.find(params[:state_id])
#county = #state.counties.find(params[:id])
#county.destroy
redirect_to state_path(#state)
end
end
states_controller.rb
This is a standard file created by rails with the scaffold generator. No changes have been made to this file. If you need it to help with this issue I will post it, but it is rather long.
schema.rb
ActiveRecord::Schema.define(version: 20141013234441) do
create_table "counties", force: true do |t|
t.string "name"
t.integer "state_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "counties", ["state_id"], name: "index_counties_on_state_id", using: :btree
create_table "states", force: true do |t|
t.string "abbr", limit: 2, null: false
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Any help is appreciated...

Never mind... I found the answer to my own question. I knew it had to be something simple. I was missing a colon in the county.rb file.
Original:
class County < ActiveRecord::Base
attr_accessible :name, state_id
Corrected:
class County < ActiveRecord::Base
attr_accessible :name, :state_id

Related

ActiveRecord::AssociationTypeMismatch in Controller#create Error in my Project

Can I get some assistance with my code which is throwing an ActiveRecord::AssociationTypeMistach in my JobsController#create.
ActiveRecord::AssociationTypeMismatch (JobCategory(#70843392) expected, got "At Home" which is an instance of String(#20478408)):
app/controllers/jobs_controller.rb:46:in `create'
I am posting data through the form
<%= form_for #job do |f| %>
<div class="row">
<div class="col-md-4 select">
<div class="form-group">
<label>Job Category</label>
<%= f.collection_select :job_category, JobCategory.order(:job_category),
:job_category, :job_category, include_blank: false, id: "job_category",
prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col-md-4 select">
<div class="form-group">
<label>Job Type</label>
<%= f.grouped_collection_select :job_type, JobCategory.order(:job_category),
:job_types, :job_category, :job_type_id, :job_type, id: "job_type",
prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col-md-4 select">
<div class="form-group">
<label>Frequency</label>
<%= f.select :recurrence, [["One Off", "One Off"], ["Daily", "Daily"],
["Weekly", "Weekly"], ["Bi-Monthly", "Bi-Monthly"],
["Once-Monthly", "Once-Monthly"]],
id: "recurrence", prompt: "Select...", class: "form-control" %>
</div>
</div>
</div>
<div><%= f.submit "Post My Job", class: "btn btn-normal" %></div>
<% end %>
Schema for Job Model
create_table "jobs", primary_key: "job_id", force: :cascade do |t|
t.string "job_title"
t.text "job_description"
t.text "key_instructions"
t.integer "budget"
t.datetime "booking_date"
t.string "recurrence"
t.boolean "is_flexible"
t.string "address"
t.boolean "active"
t.integer "user_id"
t.datetime "date_posted"
t.datetime "date_ending"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "featured"
t.boolean "premium"
t.integer "job_category_id"
t.integer "job_type_id"
t.string "job_category"
t.string "job_type"
t.index ["job_category_id"], name: "index_jobs_on_job_category_id"
t.index ["job_type_id"], name: "index_jobs_on_job_type_id"
t.index ["user_id"], name: "index_jobs_on_user_id"
end
I am posting the data from my form into my job.rb model but in the process using job_category.rb models and job_type.rb associations for additional info. The three models are related as follows
class Job < ApplicationRecord
belongs_to :user
belongs_to :job_category
belongs_to :job_type
has_many_attached :images
validates :job_category, presence: true
validates :job_type, presence: true
validates :recurrence, presence: true
end
class JobType < ApplicationRecord
has_many :job_categories
has_many :jobs
#attr_accessible :job_type
#validates :job_type, :presence => true
end
class JobCategory < ApplicationRecord
has_many :job_types
has_many :jobs
#attr_accessible :job_category
#validates :job_category, :presence => true
end
Any tips on where I am going wrong would be useful. Thanks
Based on what you've got in your question, you have two choices for job categories, (which should apply also to job types):
1) You can create a table job_categories and drop job_category_id and job_category from the jobs table.
2) The simpler option, and the one I might recommend based on very little knowledge other than you seem to be relatively new to db design and Rails, is to get rid of the JobCategory class entirely, and get rid of the belongs_to: job_category. You can also drop job_category_id from the jobs table. If you do that the rest of our code looks like it should be pretty close to just working.
The associations and extra classes you've set up are really meant for cases where you've stored that additional data in separate tables, and you are telling ActiveRecord how that data is related. Here, because a job has just one category, which is really just a single string with no additional data associated, putting everything in one table is not a bad option.

Importing CSV data into Rails app, using something other then the association "id"

I am trying to import a few CSV files into my rails app. I learned and managed to import tables into Models without association.
Now i have managed to import the data into a table that has associations, but only by entering the actual "id" number on the CSV column. Although functional, this isn't really an option because i have many tables with thousands of IDs.
My main goal is to be able to use the column in the CSV and type in the actual value (that exists in the other model it is associated with), instead of the id number.
I have a Country model and a Ports model. The Ports model is associated with country_id
Port Model
class Port < ApplicationRecord
def self.import(file)
#code
CSV.foreach(file.path, headers: true) do |row|
port = find_by_id(row["id"])
Port.create! row.to_hash
end
end
belongs_to :shipment_type
belongs_to :country
has_many :origins, :class_name => 'Rate'
has_many :destinations, :class_name => 'Rate'
end
Country Model
class Country < ApplicationRecord
def self.import(file)
#code
CSV.foreach(file.path, headers: true) do |row|
Country.create! row.to_hash
end
end
has_many :ports, dependent: :destroy
end
schema.db
create_table "ports", force: :cascade do |t|
t.string "name"
t.string "port_code"
t.integer "shipment_type_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "country_id"
t.index ["country_id"], name: "index_ports_on_country_id", using: :btree
t.index ["shipment_type_id"], name: "index_ports_on_shipment_type_id", using: :btree
end
create_table "countries", force: :cascade do |t|
t.string "name"
t.string "country_code"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shipment_types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The associations are working because i am able to manually add them view my forms i create just fine.
<%= form_for(port) do |f| %>
<% if port.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(port.errors.count, "error") %> prohibited this port from being saved:</h2>
<ul>
<% port.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :port_code %>
<%= f.text_field :port_code %>
</div>
<div class="field">
<%= f.label :shipment_type_id %>
<%= f.collection_select :shipment_type_id, ShipmentType.all, :id, :name %>
</div>
<div class="field">
<%= f.label :country_code %>
<%= f.collection_select :country_id, Country.all, :id, :country_code %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any guidance or help would be greatly appreciated. I have been going in circles with this for days now.
ADDING SAMPLE TABLE FROM CSV FILE.
A shipment_type is a ruby object, you want to send a string.
If you are needing to import relationships, add methods on the Port model like so
class Port < ApplicationRecord
def shipment_type_name
shipment_type.try(:name)
end
def shipment_type_name=(name)
self.shipment_type = ShipmentType.where(:name => name).first_or_create
end
def country_country_code
country.try(:country_code)
end
def country_country_code=(code)
self.country = Country.where(:country_code => code).first
end
end
Then in the CSV you'd send a shipment_type_name and country_country_code attributes.
You would do something similar to other relationships.
You may want to use this gem for importing CSV:
https://github.com/michaelnera/active_record_importer
It's easy to use.
Thank you everyone for the help. Below is what ended up working for me. The biggest issue i was getting was Origin and Destination. There is only one Port table, which includes a list of the Ports. Ports are used for both Origin and Destination.
class Rate < ApplicationRecord
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
rate = find_by_id(row["id"])
Rate.create! row.to_hash
end
end
belongs_to :origin, :class_name => 'Port'
belongs_to :destination, :class_name => 'Port'
belongs_to :carrier
belongs_to :shipment_category
belongs_to :unit_of_measure
has_many :additional_items
# associatiing Origin and Destination Port Code
def origin_port_code
origin.try(:port_code)
end
def origin_port_code=(port_code)
self.origin = Port.where(:port_code => port_code).first
end
def destination_port_code
destination.try(:port_code)
end
def destination_port_code=(port_code)
self.destination = Port.where(:port_code => port_code).first
end
# associating carrier name
def carrier_name
carrier_name.try(:name)
#code
end
def carrier_name=(name)
self.carrier = Carrier.where(:name => name).first
#code
end
# associating Shipment Category Name
def shipment_category_name
shipment_category.try(:name)
end
def shipment_category_name=(name)
self.shipment_category = ShipmentCategory.where(:name => name).first
end
# associating unit_of_measure name
def unit_of_measure_name
unit_of_measure.try(:name)
#code
end
def unit_of_measure_name=(name)
self.unit_of_measure = UnitOfMeasure.where(:name => name).first
#code
end
end

Rails 4: new instance is created and saved to database but not displayed in view

In our Rails 4 app, there are four models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
Here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.references :calendar, index: true, foreign_key: true
t.date :date
t.time :time
t.string :focus
t.string :format
t.string :blog_title
t.text :long_copy
t.text :short_copy
t.string :link
t.string :hashtag
t.string :media
t.float :promotion
t.string :target
t.integer :approval
t.text :comment
t.timestamps null: false
end
end
end
We have the following form to create posts:
<h2>Create a new post</h2>
<%= form_for(#post) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<tr>
<td class="field"><%= f.date_field :date, placeholder: "When do you want to publish this post?" %></td>
<td class="field"><%= f.time_field :time, placeholder: "What time do you want to publish this post?" %></td>
<td class="field"><%= f.text_field :focus, placeholder: "What is this post about?" %></td>
<td class="field"><%= f.text_field :format, placeholder: "What type of post is this?" %></td>
<td class="field"><%= f.text_field :blog_title, placeholder: "If this post is about a blog post, what is the title of the blog post?" %></td>
<td class="field"><%= f.text_area :long_copy, placeholder: "What is the copy of the post?" %></td>
<td class="field"><%= f.text_area :short_copy, placeholder: "What is the short copy of the post (to be used on Twitter for instance)?" %></td>
<td class="field"><%= f.url_field :link, placeholder: "Which link to you want to embed in this post?" %></td>
<td class="field"><%= f.text_field :hashtag, placeholder: "Which hashtag(s) do you want to you in this post?" %></td>
<td class="field"><%= f.text_field :media, placeholder: "Which media file (image, video) do you want to include in this post?" %></td>
<td class="field"><%= f.number_field :promotion, placeholder: "What advertising budget should be allocated to this post?" %></td>
<td class="field"><%= f.text_field :target, placeholder: "Who do you want to target with this post?" %></td>
<td class="field"><%= f.select(:approval, %w[Approved Needs edits To be deleted], {prompt: 'How does this post look?'}) %></td>
<td class="field"><%= f.text_area :comment, placeholder: "Any comment?" %></td>
<td><%= f.submit "Create", class: "btn btn-primary" %></td>
</tr>
<% end %>
This form is embedded into the Calendars#Show view, so that as soon as a post is created, it appears in the corresponding calendar.
EDIT 2: And here is ou PostsController:
class PostsController < ApplicationController
def create
#post = Post.create!
if #post.save
redirect_to root_url
else
render root_url
end
end
end
Currently, whenever we submit the form, a post is actually created (we checked through the console).
EDIT: Actually, the new #post is created and saved to the database but all values are set to nil. It seems we also have a problem with the saving of data from the form and into the database.
However, it does not appear in the corresponding calendar.
We believe this is due to the fact that, when a post is created, the calendar_id of the active calendar (the one in which the form is embedded in) is not added to the new post instance.
However, we don't know how to implement it:
Should we create an active_calendar method and then use it to automatically assign the active_calendar id of the active calendar to the newly created #post?
Should we simply declare the active calendar id as a variable, either in the post controller or the post model, so that we can add it when a new #post is created?
Should we include the active calendar id in the newly created #post through a hidden field in the form?
We kind of hit a wall here and are not sure of what we are doing wrong.
Any idea of how to fix this?
UPDATE: as suggested by #Fire-Dragon-DoL in the comments of his answer, we are going to use a hidden field.
Would the following code work?
<td type="hidden" value="#{#calendar.id}"><% f.number_field :calendar_id %></td>
I'm not sure how or where is stored your "active calendar" information, you definitely need it. Make it a field for User, it's a good option.
Then I would create an application controller method like
def active_calendar
# Assuming you have a method to return current logged in user
current_user.try(:active_calendar)
end
Then when creating post you can easily perform the following
attrs = post_params.merge(calendar: active_calendar)
#post = Post.new(attrs)
# Or whatever logic you want
#post.save
Use the hidden field only if user is allowed to specify different calendars than the active one, otherwise it might just be a security hole
Notice that the code I wrote is also nil-proof
You may want to nest your routes, for example:
resources :calendars do
resources :posts
end
Now change your form to also include the calendar you want to be associated with the post like:
form_for([#calendar, #post])...
Then in your posts controller create path you can find your calendar and construct your post with something like:
def create
my_calendar = Calendar.find(params[:calendar_id])
post = my_calendar.posts.create(post_params)
redirect_to root_path
end
private
def post_params
params.require(:post).permit(:date, :time, :focus, :format, :blog_title, :long_copy, :short_copy, :link, :hashtag, :media, :promotion, :target, :approval, :comment)
end

Ruby Rails update two different table columns using form

Here is my newbie story,
my table looks like this:
create_table "books", :force => true do |t|
t.string "title"
t.integer "count"
t.integer "id"
end
create_table "users", :force => true do |t|
t.string "name"
t.decimal "money"
end
user can create many books, and user using these forms to update it:
to update money:
<%= form_for(#user) do |f| %>
<%= f.number_field :money, :value => #user.money %>
<% end %>
to update book title, count etc., for each books:
<% #book.each do |book| %>
<%= form_for(book) do |f| %>
<%= f.number_field :count %>
<%= f.text_field :title %>
<% end %>
<% end %>
and im trying to do is i want update both of them. (lets say user need to update money and book title) currently it only able to update money or book info separately
model:
class Book < ActiveRecord::Base
attr_accessible :count, :title, :id
belongs_to :user
end
any idea? Thanks!
It's not that simple to explain in a single answer. I suggest you to have a look at the following Railscasts to get an idea.
http://railscasts.com/episodes/198-edit-multiple-individually
http://railscasts.com/episodes/165-edit-multiple-revised

Limiting :association results accessible on a form in my rails app

I'm creating a very basic e-commerce app to teach myself rails and I've hit my first snag:
I created some associations so that merchants could assign categories to products they post and that works fine the way I've done it, however, the way it works now, when a merchant is creating a new product all the categories from the categories table are available to select. Hypothetically, if I wanted to limit that list to only categories that that specific merchant has created or been associated with, I presume taken from the categories_merchants table, how would I go about doing that?
I've shown the relevant tables and a form below to show what I've done but left out the models, however I have all the appropriate associations in them:
Products Table:
create_table :products do |t|
t.integer :merchant_id
t.string :title
t.text :body
t.date :publish_date
t.date :expiry_date
t.timestamps
Categories Table:
create_table :categories do |t|
t.string :name
t.timestamps
Categories_Products Table:
create_table :categories_products, :id =>false do |t|
t.integer :category_id
t.integer :product_id
end
add_index :categories_products, [:category_id, :product_id]
Categories_Merchants Table:
create_table :categories_merchants, :id =>false do |t|
t.integer :category_id
t.integer :merchant_id
end
add_index :categories_merchants, [:category_id, :merchant_id]
New Products form:
<%= simple_form_for(#product) do |f| %>
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :body %>
<%= f.association :categories, :as => :collection_select %>
<%= f.input :publish_date %>
</div>
<% end %>
Thanks in advance ;)
You could do something like this:
<%= f.association :categories,
:as => :collection_select,
:collection => Category.includes(:categories_merchants).where("categories_merchants.merchant_id = ?", merchant_id) %>

Resources