I am new to rails and decided to make make an application that shows the weather. I followed this tutorial and GoRails Rails API video course. When I run server (path: http://127.0.0.1:3000/api/v1/locations), I see undefined method respond_to for Api::V1::LocationsController:Class error. The same is with http://127.0.0.1:3000/api/v1/locations/1. I googled a lot, but didn't see an appropriate answer. I am leaving code here. Locations is for the city and Recordings is for temperature. I am also leaving schema here if this is the case. A screen of directories tree is attached.
I'VE NEVER BEEN SO STUCK
routes.rb
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :locations
end
end
resources :locations
root "locations#index"
end
locations_controller:
module Api
module V1
class LocationsController < ApplicationController
respond_to :json
def index
respond_with LocationsController.all
end
def show
respond_with find_location
end
private
def find_location
#location = LocationsController.find(params[:id])
end
end
end
end
recordings_controller
class RecordingsController < ApplicationController
end
application_controller
class ApplicationController < ActionController::Base
end
Models:
location.rb
It is also not able to find these fields in db for some reason...
class Location < ApplicationRecord
belongs_to :recording
validates_uniqueness_of :name
end
recording.rb
class Recording < ApplicationRecord
has_many :locations
end
schema:
ActiveRecord::Schema[7.0].define(version: 2022_10_20_135036) do
create_table "locations", force: :cascade do |t|
t.integer "recording_id", null: false
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["recording_id"], name: "index_locations_on_recording_id"
end
create_table "recordings", force: :cascade do |t|
t.integer "temp"
t.string "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "locations", "recordings"
end
I thought to use here one-to-many association. As I thought, one city has one temperature and one temperature has loads of cities.
The tutorial you are following is quite old and partly outdated. The respond_to method which is in the first line of the Api::V1:: LocationsController was removed in Ruby on Rails 5.0 about six years ago.
But the good news it that its functionality has been extracted to the responders gem. That means when you add that gem to your application's Gemfile then it might work after running bundle install and restarting the server.
Note. I wrote it might work because it is possible that there are other incompatibilities between the tutorial and your current, up-to-date Ruby on Rails version 7.0.
Related
My goal is for users to add individual games pulled from an API gem (https://github.com/games-directory/api-giantbomb) to their personal library. I want users to be able to browse other people's libraries. I have the games showing up via search along with a show page for each game.
I am running into two problems: can't add games to a user's library and can't view other people's library.
Here is my games controller:
class GamesController < ApplicationController
#search for games
def index
#games = GiantBomb::Search.new().query(params[:query]).resources('game').limit(100).fetch
end
#Shows data for individual games
def show
#game = GiantBomb::Game.detail(params[:id])
end
#Adding and removing games to a user's library
def library
type = params[:type]
#game = GiantBomb::Game
if type == "add"
current_user.library_additions << #game
redirect_to user_library_path, notice: "Game was added to your library"
elsif type == "remove"
current_user.library_additions.delete(#game)
redirect_to root_path, notice: "Game was removed from your library"
else
# Type missing, nothing happens
redirect_to game_path(#game), notice: "Looks like nothing happened. Try once more!"
end
end
private
def game_params
params.require(:game).permit(:name, :search, :query)
end
end
When I try to add a game to my library, I get "Game(#70231217467720) expected, got GiantBomb::Game which is an instance of Class(#70231150447440)". So my #game is incorrect but I am not sure what should be there instead.
Even if I could add the game to my library, I can't view other user's libraries. Here is my current controller.
class LibraryController < ApplicationController
#before_action :authenticate_user!
def index
#library_games = User.library_additions
end
end
I get 'undefined method library_additions' even though it is in the model. If I change User to current_user I can see the page, but that means users can only see their page and not others.
Here are my game, user, and library model:
class Game < ApplicationRecord
has_many :libraries
has_many :added_games, through: :libraries, source: :user
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :games
has_many :libraries
has_many :library_additions, through: :libraries, source: :game
end
class Library < ApplicationRecord
belongs_to :game
belongs_to :user
end
I made my library a join table for users and games but I am thinking I didn't do it correctly. Here is my schema:
ActiveRecord::Schema.define(version: 2020_11_19_143536) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "games", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "search"
end
create_table "libraries", force: :cascade do |t|
t.integer "user_id"
t.integer "game_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
end
Am I missing a migration or do I need the rework the models and controllers?
[edit] Here are my routes, I am getting a pathing error when I try to add a game.
Rails.application.routes.draw do
devise_for :users
resources :games do
member do
put "add", to: "games#library"
put "remove", to: "games#library"
end
end
resources :library, only:[:index]
root to: 'pages#home'
get '/search', to: 'games#search', as: :search
get '/games', to: 'games#index', as: :index
get '/user/:id', to: 'user#show'
get '/user/:id/library', to: 'library#index', as: :user_library
end
Here, the error clearly states it is expecting an instance of Game not GiantBomb::Game, so you have to create one.
#game = Game.new(name: 'some name', other fields ....)
if type == "add"
current_user.library_additions << #game
About the other error you can only call association methods on an instance not on the class itself
def index
# You could get the user'id through params for example
#library_games = User.find(params[:user_id]).library_additions
end
I am currently learning rails and in the process of building my first rails project. I created a :restaurant model (along with other models - bookings and user) with several attributes which include :city. Here's my schema:
create_table "restaurants", force: :cascade do |t|
t.string "name"
t.string "city"
t.string "website"
t.string "phone_number"
t.integer "ratings"
t.integer "capacity"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
In my root '/' page, I am displaying the unique city values as a list with links. I was hoping that the user can browse restaurants by clicking on the city that they are in or planning to visit (ideally with a link '/restaurants/#{city}' and through that end up in a page with the list of restaurants in that city.
I have been trying to figure out how to do this currently my relevant routes look like this:
resources :restaurants do
resources :bookings
end
I tried creating :city as a nested resource but this ended up with url '/restaurants/:restaurant_id/:city' which isn't what I'm trying to achieve.
But most importantly, I cannot work out how the 'city' that the user clicked in the root page can lead to the page with all the restaurants in that city.
Any advice would be most helpful.
Thank you.
Routes are extremely flexible and give you a lot of power.
First option: I'd suggest the more traditional Rails way: Separate out your cities into their own model and relate them to a restaurant.
Something like this:
class City < ApplicationRecord
has_many :restaurants, inverse_of: :city
...
end
class Restaurant < ApplicationRecord
belongs_to: city, inverse_of: :restaurants
...
end
Then, I'd move your database around a bit:
create_table :cities do |t|
t.string :name, null: false
t.timestamps
end
create_table :restaurants do |t|
t.string :name
t.references :city
t.string :website
t.string :phone_number
t.integer :rating
t.integer :capacity
end
This will get you on the right track for nested routing like:
/cities/:city_id/restaurants
Second option is to wander off the RESTful path and play with the flexibility of routes:
(I'm suggesting getting away from /restaurants/:city and just use /:city, but the idea is the same)
# routes.rb
# warning! Put this towards the very end of your file. Even then, any URL you try to hit that fits
# this pattern will get sent to this controller action. e.g. "yoursite.com/badgers"
# you'll need to explore handling RecordNotFound and redirecting someplace else
get '/:city', to: 'restaraunts#by_city', as: 'restaurants_by_city'
Now in your Restaurants controller:
class RestaurantsController < ApplicationController
...
def by_city
city = params[:city] # this will be whatever is in the url
#restaurants = Restaurant.where(city: city)
# you'll need some error handling:
redirect to root_path if #restaurants.empty?
...
end
end
I have just made a new model named Comments and now I'm trying to make a view so that user can leave comments from the UI(very basic stuff here). But, I'm running into this error uninitialized constant CommentsController::Comments for the life of me I can't figure out why it's throwing this error and not just rendering the page when it's clicked?
I'm going to post all the applicable code and error for clarity.
MODEL:
class Comment < ActiveRecord::Base
belongs_to :subscriber
end
CONTROLLER:
class CommentsController < ApplicationController
def new
#comments = Comments.new
end
end
ROUTES
Rails.application.routes.draw do
devise_for :users
resources :subscribers, except: :show
resources :comments
SCHEMA:
create_table "comments", force: :cascade do |t|
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "fav_drink"
t.string "visit_time"
end
VIEW:
<h2>Let us get to know you more <%= link_to 'Comment', new_comment_path %> </h2>
This is the link I've set up to click the the page where the user will leave a comment.
ERROR:
Let me know if you need to see anymore code. Thank you!
I just made a silly mistake and called Comments.new instead of Comment.new
Models are referenced singularly as they are classes. You have made a mistake of Comments.new but it should be Comment.new.
Here is my Schema file
ActiveRecord::Schema.define(version: 20150917104809) do
create_table "customers", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
end
create_table "orders", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "order_date"
t.integer "customers_id"
end
end
Here are the models
class Customer < ActiveRecord::Base
has_many :orders, dependent: :destroy
end
class Order < ActiveRecord::Base
belongs_to :customer
end
The migration file for association is
class AddForeignKeyToOrders < ActiveRecord::Migration
def change
add_reference :orders, :customers
end
end
Following the simple rails assoc example in the link
I created a customer record using the command
Customer.create(name: 'Someone')
and now trying to create the order
#order = #customer.orders.create(order_date: Time.now)
Am getting an error with a NilClass
NoMethodError: undefined method `orders' for nil:NilClass
May be another pair of eyes of help to tell what am I doing wrong.
You need to assign the return value of create to #customer:
#customer = Customer.create(name: 'Someone')
#order = #customer.orders.create(order_date: Time.now)
It also looks like you have a typo in your schema. It should be:
t.integer "customer_id"
EDIT: Your migration file is incorrect. It should be:
class AddForeignKeyToOrders < ActiveRecord::Migration
def change
add_reference :orders, :customer
end
end
See the add_reference API docs.
To add to #ghr's answer, your error is thus: for nil:NilClass
This means that you're trying to call a method on an object which doesn't exist. Ruby actually assigns nil classes to an NilClass object, so it's errors aren't easily recognized unless you've spent time with them.
--
You have to assign Customer.create... to a variable, otherwise Rails doesn't have the data stored for you to work with:
#customer = Customer.create(name: "Someone")
#customer.orders.create
As a note, you don't need order_date in your Order model - created_at will handle that. If you removed the order_date column from your table, you'd be able to call the following:
#app/models/order.rb
class Order < ActiveRecord::Base
belongs_to :customer
alias_attribute :date, :created_at
end
#customer.orders.first.date
I have an article model that should belong to a section. I'm having trouble making this connection work and I receive "Undefined Method" errors when attempting Section.article or Article.section in rails console.
How can I tie these models together to print all articles of a particular section and verify their connection?
I've implemented many solutions from answers and posts and may have mixed things up.
Thank you for your help!
Models (I've also had versions with a forgeign_key or reference entries):
class Article < ActiveRecord::Base
belongs_to :section
end
class Section < ActiveRecord::Base
has_many :articles
end
Migration to update tables:
class AddSectionRefToArticles < ActiveRecord::Migration
def change
add_reference :articles, :section, index: true
end
end
Schema.rb
ActiveRecord::Schema.define(version: 20141107123935) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "articles", force: true do |t|
t.string "title"
t.string "body"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "section_id"
end
add_index "articles", ["section_id"], name: "index_articles_on_section_id", using: :btree
create_table "sections", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
What are you actually running on the command line? Section.article or Article.section will not work.
You need to run the relation methods on an instance, not the class itself.
section = Section.create(name: 'Section 1')
section.articles << Article.new(title: 'My article')
section.articles # returns an array of articles belonging to the Section 1 object
Article.last.section # returns the Section 1 object
You attempt to use class methods (Section.article or Article.section), whereas associations are defined as instance methods. So that, to call an association you have to call it on an object, e.g: Section.first.articles or Article.last.section