Newbie on RoR and a little confused on whats happening.
I am following the tutorial here: https://www.javatpoint.com/ruby-on-rails-crud to create a crud application. On step 17 I have to run the "rake db:migrate" in my terminal. Each time I get the error message "NameError: uninitialized constant Products
mount Products::ProductsAPI => '/api/products'"
I worked through suggestion here:https://stackoverflow.com/questions/35978598/rake-aborted-nameerror-uninitialized-constant-users in an older post. All of the other classes and modules are labelled as "Products" except for the Model product.rb (\crud\app\models\product.rb). I copied the code exactly and updated the gems using "gem update --system" incase it was the Grape gem causing the issue. I'll share the files below:
My Products Controller
class ProductsController < ApplicationController
# GET method to get all products from database
def index
#products = Product.all
end
# GET method to get a product by id
def show
#product = Product.find(params[:id])
end
# GET method for the new product form
def new
#product = Product.new
end
# POST method for processing form data
def create
#product = Product.new(product_params)
if #product.save
flash[:notice] = 'Product added!'
redirect_to root_path
else
flash[:error] = 'Failed to edit product!'
render :new
end
end
# GET method for editing a product based on id
def edit
#product = Product.find(params[:id])
end
# PUT method for updating in database a product based on id
def update
#product = Product.find(params[:id])
if #product.update_attributes(product_params)
flash[:notice] = 'Product updated!'
redirect_to root_path
else
flash[:error] = 'Failed to edit product!'
render :edit
end
end
# DELETE method for deleting a product from database based on id
def destroy
#product = Product.find(params[:id])
if #product.delete
flash[:notice] = 'Product deleted!'
redirect_to root_path
else
flash[:error] = 'Failed to delete this product!'
render :destroy
end
end
# we used strong parameters for the validation of params
def product_params
params.require(:product).permit(:name, :price, :old_price, :short_description, :full_description)
end
end
My Product Model:
class Product < ApplicationRecord
validates :name, presence: true
validates :price, presence: true, numericality: {:greater_than => 0}
validates :short_description, presence: true
end
My migrate db table:
class CreateProducts < ActiveRecord::Migration[7.0]
def change
create_table :products do |t|
t.string :name
t.decimal :price
t.text :short_description
t.text :full_description
t.timestamps
end
end
end
My routes file:
Rails.application.routes.draw do
get 'products/index'
get 'products/show'
get 'products/new'
get 'products/create'
get 'products/edit'
get 'products/update'
get 'products/destroy'
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
resources :products
root 'products#index'
# Defines the root path route ("/")
mount Products::ProductsAPI => '/api/products'
# root "articles#index"
end
I followed the steps to the letter but I am unable to run the rake db:migrate.
I changed the migration filename and class name so that they were matching - didnt run
I updated the location of the route in the routes file, did not run and got the same error.
I reviewed and updated the code to match exactly the steps from the javtpoint tutorial and its not running.
I tried running the terminal in admin incase it helped
I put the full location of the folder in the routes file instead of "ProductsAPI => '/api/products'"I had C:\Users\Desktop\CRUD\CRUD\crud\app\api\products
Also, updated the gems and and ran bundle install after. Everything is working as designed as far as step 17 "rake db:migrate".
Really appreciate your help!
It's not able to find this file from step 15:
app/api/products/products_api.rb
You can quickly verify this from the Rails console:
$ rails console
[1] development(main)> Products::ProductsAPI
NameError: uninitialized constant Products
Does this file exist?
Also, I don't believe that the config.autoload_paths configuration from Step 4 is necessary these days. Any custom directories under /app are automatically loaded in Rails 7:
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#config-autoload-paths
Note:
This tutorial looks like it's based off of Rails 4 or 5 from around 2015, and you are using Rails 7. You might run into other issues trying to implement this tutorial in Rails 7. I would recommend finding a more modern tutorial.
Related
everyone! I am new to rails and working on Codecademy tutorials. But I wanted to see if I can run the same app on my mac using VS Code and got into some roadblocks. The application is basically to create a form that takes in messages and displays it (in the index view). I wanted to explore changing the names of controller and model to what I want and guess I messed up the internal routing. Following is the controller (messagec)
class MessagecController < ApplicationController
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
render 'new'
end
end
private
def message_params
params.require(:message).permit(:content)
end
end
THe following is the model (messagesmo1)
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
The following is the routes.rb file
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'message_mo1s#create'
The following is the code in create.html.erb file
<%= form_for(#messages2) do |f| %>
<div class = "field">
<%= f.label :message %><br>
<%= f.text_area :content %>
</div>
<div class = "actions">
<%= f.submit "Create" %>
</div>
<% end %>
I am able to see the message list and able to go to create new message page. But when I submit the form, I am getting the following Routing error:
uninitialized constant MessageMo1sController Did you mean? MessagecController MessagesMController
My first questions is:
1) What am I missing in the routes.rb file?
2) Is there any rule between naming the model similar to that of the controller?
I just replicated all of the above, I think there are many things to keep in mind.
Your model file must be of name messagesmo1.rb and in this model:
class MessagesMo1 < ApplicationRecord
end
Your controller file should be of name messagec_controller.rb and in it:
def index
#messages1 = MessagesMo1.all
end
def new
#messages2 = MessagesMo1.new
end
def create
#messages2 = MessagesMo1.new(message_params)
if #messages2.save #tells if the object is saved successfully in db or not
flash[:success] = "Great! Your post has been created!"
redirect_to '/messages'
else
flash.now[:error] = "Fix your mistakes, please."
redirect_to '/messages/new'
end
end
private
def message_params
params.require(:messages_mo1).permit(:content)
end
In the above point, look at the message_params part, it must be :messages_mo1 and not :message
No changes required in _form.html.erb file
Your migration file must be of name timestamp__create_messages_mo1s.rb and it must have:
class CreateMessagesMo1s < ActiveRecord::Migration[6.0]
def change
create_table :messages_mo1s do |t|
t.text :content
t.timestamps
end
end
end
In your routes.rb file, change the last route:
get '/messages' => 'messagec#index'
get '/messages/new' => 'messagec#new'
post 'messages' => 'messagec#create'
post 'messages_mo1s' => 'messagec#create'
Make sure all your links are updated in index.html.erb, in show.html.erb and in new.html.erb -> Like links to show, delete, edit etc. Or if your just testing remove these links.
After making above changes, run rails db:drop db:create db:migrate as it will clean your DB of old migration.
That's it, now everything should work. The main problem is naming convention should be standard across all files. So it's better to use standard convention.
It finally worked. Following are the 2 changes:
1) Instead of <%= form_for(#messages2) do |f| %>, I used a URL parameter
<%= form_for(#messages2, url:'/messages/') do |f| %>
2)As #cdadityang mentioned, I updated the params to params.require(:messages_mo1).permit(:content)
without the URL being given explicitly, I think the rails is assuming '/message_mo1' are the path. So the URL is basically taking it to 'messagec#create'
baby Ruby coder here.
I followed the steps here:https://masteruby.github.io/weekly-rails/2014/08/05/how-to-add-voting-to-rails-app.html#.XMFebOhKg2w to upload this act_as_votable gem however when I refresh my site to see if it works I get the following error: undefined method `acts_as_votable' for #<Class:0x00007f65df881b38>
The code does not seem to like the fact that I have put act_as_votable in my model I would like to use it on.
The error in my console also indicates that something is wrong in my controller. Do I need to define something there too?
Thanks in advance,
Angela
Model I want to use the act_as_votable gem on, you can see i have added it as the instructions instructed:
class Hairstyle < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
validates :name, presence: true
validates :description, presence: true
validates :category, presence: true
acts_as_votable
mount_uploader :photo, PhotoUploader
end
My hairstyles controller with the 'upvote' method at the end:
class HairstylesController < ApplicationController
def index
if params[:category].present?
#hairstyles = Hairstyle.where(category: params[:category])
elsif params[:search].present?
#hairstyles = Hairstyle.where('name ILIKE ?', '%#{params[:search]}%')
else
#hairstyles = Hairstyle.all
end
end
def show
#hairstyle = Hairstyle.find(params[:id])
#comment = Comment.new
end
def new
#hairstyle = Hairstyle.new
end
def create
#hairstyle = Hairstyle.create(hairstyle_params)
#hairstyle.user = current_user
if #hairstyle.save!
redirect_to hairstyle_path(#hairstyle)
else
render 'new'
end
end
def edit
#hairstyle = Hairstyle.find(params[:id])
end
def update
#hairstyle = Hairstyle.find(params[:id])
#hairstyle.update(hairstyle_params)
redirect_to hairstyles_path
end
def destroy
#hairstyle = Hairstyle.find(params[:id])
#hairstyle.destroy
redirect_to hairstyles_path
end
def upvote
#hairstyle = Hairstyle.find(params[:id])
#hairstyle.upvote_by current_user
redirect_to hairstyles_path
end
private
def hairstyle_params
params.require(:hairstyle).permit(:name, :description, :category, :location, :stylist, :photo, :video_url, :photo_cache)
end
end
My index file i'd like to display the gem on:
<% #hairstyles.each do |hairstyle| %>
<%= link_to "upvote", like_hairstyle_path(hairstyle), method: :put%>
<%end %>
</div>
Here is my repo if needed :https://github.com/Angela-Inniss/hair-do
It looks like you didn't run all 4 setup steps:
add 'acts_as_votable' to gemfile
Then run from terminal:
bundle install
rails generate acts_as_votable:migration
rake db:migrate
I cloned & setup your repo & I saw a few things going on that might be the cause:
The original error I got in this clone was due to act_as_taggable on your models. Your models should be annotated as acts_as_taggable (plural)
When I was initially testing, I wasn't logged in. This silently fails, which makes it seem like upvote isn't work. You might want to disable those hearts unless a user is logged in
Your html/erb template has some commented out code and such. This might be causing the link/URL to get swallowed and be non-clickable. I resolved it by deleting all styling & formatting except the link I was testing. I like using Haml to help reduce these kinds of nesting errors
I didn't run into your class error, but I would suggest running spring stop and trying to start your server again (I disable spring on all of my rails projects)
I'm following the Rails tutorial and making changes where appropriate, with the intention that my tutorial project will become a full-fledged production app after the completion of the tutorial.
I've run into a snag with the second model portion of the tutorial. Here is how I've written my second model.
In my policy.rb:
class Policy < ApplicationRecord
has_one :insured
end
In my insured.rb:
class Insured < ApplicationRecord
belongs_to :policy
end
In my routes.rb:
resources :policies do
resource :insured
end
In my insureds_controller.rb:
def create
#policy = Policy.find(params[:policy_id])
# next line is raising the error
#insured = #policy.insured.create(insured_params)
redirect_to #insured
end
private
def insured_params
params.permit(:name, :address, :phone, :email)
end
I've inspected the #policy object with render plain: #policy.inspect and can confirm that ActiveRecord is retrieving the policy correctly. When I inspect the attributes of #policy, using render plain: #policy.attribute_names.inspect, I don't see an insured attribute, which I thought Rails was supposed to automatically manage for me. In the tutorial, an article has_many :comments, and a comment is supposedly easily created and associated with the parent article with this call: #article.comments.create(comment_params). I also noticed that the tutorial uses params.require(:comment).permit(...) while I have to use params.permit(...), after inspecting the params hash I saw that the :insured attributes existed in the top-level of the hash, instead of being tied to an :insured key within the hash.
I tried manually saving and assigning the #insured object like so:
def create
#policy = Policy.find(params[:policy_id])
#insured = Insured.new(insured_params)
if #insured.save
#policy.insured = #insured
redirect_to #insured
end
end
Only to run into the following error in my .../insureds/new.html.erb:
<h1>New Insured</h1>
<h1><%= #policy.policy_number %></h2>
<%= render 'form' %>
<%= link_to 'Cancel', policy_path(#policy) %>
Which derives from my partial form .../insureds/_form.html.erb:
# the following line raises the error
<%= form_with model: #insured, local: true do |form| %>
# html omitted for brevity
<% end %>
Error: 'undefined method insureds_path'. This is weird because when I inspect the HTML I can see the form action for this view is /policies/[:id]/insured.
Sorry for the massive wall of text, I wanted to show you guys that I did try to figure out what is going wrong.
There is an error in your config/routes.rb file:
resources :policies do
# change it for:
collection do
get 'insured', to: 'policies#show_insured', as: 'show_policy_insured'
# maybe unnecessary to be here
# get 'insured/new', to: 'insureds#new', as: 'new_policy_insured'
# post 'insured/create', to: 'insureds#create', as: 'create_policy_insured'
# delete 'insured/delete', to: 'insureds#delete', as: 'delete_policy_insured'
end
end
# add resources here
resources :insureds
In policy_controller.rb:
def show_insured # 'policy/:id/insureds/
end
In insureds_controller.rb:
def show # '/insureds/:id'
end
def create
...
redirect_to show_policy_insured && return if #insured_policy
end
# before_filter or before_action
#policy = Policy.find(params[:id])
#insured_policy = #policy.insured
Check it and run this to see your routes:
$ bundle exec rake routes
get /policies/:id/insured => 'policies_controller#show_insured'
get /insureds/:id => 'insureds_controller#show'
get /insured/new => 'insureds_controller#new'
post /insureds/create => 'insureds_controller#create'
delete /insureds/:id/delete => 'insureds_controller#delete'
#maguri, that's not all necessary. The stumbling block I was running into was that Rails couldn't automatically determine the correct routes. When I provided my own urls in the form_with declarations, everything went smoothly.
Observe the following change in my _form.html.erb for the Insured model, which belongs_to Policy, which has_one Insured.
<%= form_with model: #insured, url: policy_insured_path(#policy) local: true do |form| %>
In my updated insureds_controller.rb file, using #Phlip's suggestion:
def create
#policy = Policy.find(params[:policy_id])
#insured = #policy.create_insured(insured_params)
if #policy.insured.save
redirect_to policy_insured_path(params[:policy_id])
else
render 'new'
end
end
This allows me to keep routes.rb clean and simple:
resources :policies do
resource: insured
end
Thank you for your answer, it helped me discover the problem was with my routes.
My app allows users to log in and have sessions. I have a user controller and a sessions controller, mainly developed from railscasts "authorization from scratch".
I recently added the ability to upload files to S3 using a jquery uploader... Again lots of this comes from railscasts "uploading to amazon S3".
The problem is my uploads are not user specific. Right now my "upload" controller has an "authorize" before_filter to ensure you must be logged in to access the uploader; however once a user uploads a file, ALL users see the upload! Not good! I need to ensure users only see the respective files they upload.
I've tried a few things but none seem to work. I'm looking for some direction on how to ensure users only see the files they upload. I'm following different railscasts and rails documentation on nesting resources (I think that is how I have to do this?) but I keep missing something as there seems to be lots of changes that I don't 100% understand. I fix one error, then hit another, and am wondering if I'm even going down the right path or maybe I'm missing something?
The way I thought this should work is to first nest the resource:
resources :users do
resources :cust_uploads
end
Then I modified the models as below and ran "rake db:migrate" to tie them together... I may need to manually modify a migration file with a foreign id field?:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :password, :password_confirmation
validates_uniqueness_of :email
has_many :CustUploads
end
class CustUpload < ActiveRecord::Base
attr_accessible :cust_file_url, :name
before_create :default_name
belongs_to :User
def default_name
self.name ||= File.basename(cust_file_url, '.*').titleize if cust_file_url
end
end
This gives me tons of path errors which I'm fighting through now... as my new_cust_upload_path is probably something like new_user_cust_upload_path
I also think my forms and controllers need lots of modification....
I'm using form_for
<%= form_for(#cust_upload) do |f| %>
Which I think should now be #user.cust_upload?
controllers at the moment:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
end
class CustUploadsController < ApplicationController
before_filter :authorize
def index
#cust_uploads = CustUpload.all
end
def show
#cust_upload = CustUpload.find(params[:id])
end
def new
#cust_upload = CustUpload.new
end
def create
#cust_upload = CustUpload.create(params[:cust_upload])
end
def edit
#cust_upload = CustUpload.find(params[:id])
end
def update
#cust_upload = CustUpload.find(params[:id])
if #cust_upload.update_attributes(params[:cust_upload])
redirect_to #cust_upload_url, notice: "Cust upload was successfully updated."
else
render :edit
end
end
def destroy
#cust_upload = CustUpload.find(params[:id])
#cust_upload.destroy
redirect_to cust_uploads_url, notice: "Cust Upload was successfully destroyed"
end
end
Any direction will be greatly appreciated. I've been through many tutorials and can make simple things work from scratch, I just can't seem to integrate this functionality with my existing app. There is something here I can't wrap my brain around and I'm hoping someone can provide me with that Eurika moment! Thanks
EDIT
routes.rb and my models appear to have the appropriate connections (code below). When in terminal I type "rake routes" I get a list as expected (see below) however I get and error: "No route matches {:action=>"show", :controller=>"cust_uploads"}" for a link with user_cust_uploads_path. There is a show template in the cust_uploads path and rake routes says it exists! What am I missing?
user_cust_uploads GET /users/:user_id/cust_uploads(.:format) cust_uploads#index
POST /users/:user_id/cust_uploads(.:format) cust_uploads#create
new_user_cust_upload GET /users/:user_id/cust_uploads/new(.:format) cust_uploads#new
edit_user_cust_upload GET /users/:user_id/cust_uploads/:id/edit(.:format) cust_uploads#edit
user_cust_upload GET /users/:user_id/cust_uploads/:id(.:format) cust_uploads#show
Considering you want to achieve
Users who upload stuff should only see them.
Why don't you associate the uploads with a specific user id and then when showing them in the view pull them from their own id (current_user.uploads)
I have been debugging this the entire day.
I have two models in my application: teaClass & tea. In teaclass.rb, I have
has_many :teas
In tea.rb, I have 'belongs_to :teaclass`.
I try to make the url looks like this "..teaclasses/:id/teas/:id"; so in teas_controller.rb, I put before_filter :get_teaClass
def show
#tea = #teaclass.teas.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #tea }
end
end
def new
if #teaclass.teas
#tea = #teaclass.teas.new
#teaclass.teas << #tea
##tea = Tea.new
else
flash[:notice=>"failed"]
#tea = Tea.new
#teaclass.teas << #tea
end
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #tea }
end
end
def get_teaClass
begin
#teaclass = Teaclass.find(params[:teaclass_id])rescue
redirect_to teaclass_path, :notice => "Teaclass Required!"
end
end
But I keep getting an error saying "unknown attribute: teaclass_id"
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2906:inassign_attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `each'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2902:in `assign_attributes'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2474:in `initialize'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_collection.rb:380:in `new'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_collection.rb:380:in `send'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_collection.rb:380:in `method_missing'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/base.rb:2178:in `with_scope'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_proxy.rb:207:in `send'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_proxy.rb:207:in `with_scope'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/association_collection.rb:376:in `method_missing'
/home/jianpchen/repo/Teashop/app/controllers/teas_controller.rb:31:in `new'
Can anyone help me on this? Thank you.
I try to make the url looks like this "..teaclasses/:id/teas/:id"; so in teas_controller.rb, I put before_filter :get_teaClass
This only matters if you are trying to get nested routes setup but id doesn't sound like you are doing that.
What you need to do is actually add the foreign id to the database. So check schema.rb and make sure that column exists.
Here is what you need to do.
Make sure you actually have the foreign in the database table tea.
If you do skip to the next step where I will show you a better way to write your code.
If you do not have 'tea_class_id' as an integer value on your 'tea' table you need to add it.
script/generate migration add_tea_class_id_to_tea
rake db:migrate
Now This is how your code should be written
routes.rb
map.resources :teas
map.resources :teas_classes
Models
models/tea.rb
class Tea < ActiveRecord::Base
belongs_to :tea_class
end
tea_class.rb
class TeaClass < ActiveRecord::Base
has_many :teas
end
controllers
teas_controller.rb
def new
#tea = Tea.new
end
def show
#tea = tea.find(params[:id)
end
def create
#tea = Tea.new(params[:tea])
if #tea.save
redirect_to teas_path
else
render :action => 'new'
end
end
views
This is really important. Make sure you are passing the :tea_class_id as a parameter when you create a tea otherwise it doesn't know how to make the association. It's kinda behind the stage because you send params[:tea] but it is in those parameters where the tea_class_id is actually sent.
So... in your view you need to have some sort of way for users to choose a category or as you have it tea class and that is usually done with a select box when it is a one to many association.
new.html.erb
<% form_for(#tea) do |t| %>
<%= t.collection_select :tea_class_id TeaClass.all, :id, :name %>
<% end %>
Make sure you have tea classes to actually fill the collection_select method. Google that plus rails api if you don't get what's going on.
Nested Routes (on the side)
Looked like you were trying to get the routes like teaclasses/:id/teas/:id. This is called nested routing and you will want to set that up in your routes.rb
map.resources :tea_classes_ do |tea_classes|
tea_classes :teas
end
map.resources :teas
Then you can link to teas_classes/pour/teas/chinese. You should know this command rake routes. It will help you understand how the paths work.
But if you just want the link to get going it should be like this:
<%= link_to "Teas", tea_classes_teas_path(#tea_class)%>
You need to supply #teas do the link because it takes the id from that and when you click it gives it to the teas_controller' asparams[:teas_class_id]`. You don't need to do anything without. It will automatically be in the url.
If you look at your logs, you'll see what the params has contains. Given your code and explanation, there is not key teaclass_id in params. You most likely are looking for :id. If you want to access :teaclass_id, you'll probably need to set that up in your route
match 'controller/:teaclass_id' => 'controller#show'