Create actions off of collection - ruby-on-rails

I want to create some actions dynamically, something like the below.
But as the code is not in a method I get the following error: "undefined local variable or method"
Is this at all possible, and if so - how?
class Post < ActiveRecord::Base
CATEGORIES = [:music,:movies,:art,:jokes,:friends,:whatever].freeze
end
class PostsController < ApplicationController
Post::CATEGORIES.each do |category|
eval <<-INDEX_LIKE_ACTIONS
def #{category}
#posts = Post.where( category: '#{category}' )
render :index
end
INDEX_LIKE_ACTIONS
end
end
resources :posts do
collection do
Post::CATEGORIES.each {|category| get category.to_s}
end
end

You can use ruby's define_method
Post::CATEGORIES.each do |category|
define_method category do
#posts = Post.where(category: category.to_s)
render :index
end
end

Related

Rails new initialized object create an empty record

I have two model called TodoList and TodoItem. In the TodoItem index page, i'm showing new form and list of todo items. Everything works perfect But it generate an empty record while in browser.
class TodoItem < ApplicationRecord
belongs_to :todo_list
end
class TodoList < ApplicationRecord
has_many :todo_items, dependent: :destroy
end
controllers have:
class TodoItemsController < ApplicationController
def index
#todo_list = TodoList.find(params[:todo_list_id])
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
end
def create
#todo_list = TodoList.find(params[:todo_list_id])
#todo_item = #todo_list.todo_items.new(params.require(:todo_item).permit(:description, :complete_at))
if #todo_item.save
redirect_to todo_list_todo_items_path(#todo_list)
end
end
end
index.html.erb
<div>
<div>
<% form_with(model: [#todo_list, #todo_item], local: true) do |f| %>
<% f.text_field :description %>
<% f.submit %>
<% end %>
</div>
<ul>
<% #todo_items.each do |todo_item| %>
<li><%= todo_item.description %></li>
<% end %>
</ul>
</div>
class TodoItemsController < ApplicationController
# use callbacks instead of repeating yourself
before_action :set_todolist, only: [:new, :create, :index]
def index
#todo_items = #todo_list.todo_items
#todo_item = TodoItem.new
end
def create
#todo_item = #todo_list.todo_items.new(todo_list_params)
if #todo_item.save
redirect_to [#todo_list, :todo_items]
else
render :new
end
end
private
def set_todolist
#todo_list = TodoList.find(params[:todo_list_id])
end
# use a private method for your params whitelist for readibility
# it also lets you reuse it for the update action
def todo_list_params
params.require(:todo_item)
.permit(:description, :complete_at)
end
end
You where setting a different instance variable (#new_todo) in you index action. The polymorphic route helpers that look up the route helpers from [#todo_list, #todo_item] call compact on the array. So if #todo_item is nil its going to call todo_lists_path instead - ooops!
You alway also need to consider how you are going to respond to invalid data. Usually in Rails this means rendering the new view. If you are rendering the form in another view such as the index view it can get kind of tricky to re-render the same view as you have to set up all the same data as that action which leads to duplication.
It seems #new_todo has been added to #todo_items somehow in index action:
def index
#todo_items = #todo_list.todo_items
#new_todo = #todo_list.todo_items.new
# The above line has a side effect: #todo_items = #todo_items + [#new_todo]
end
I'm not sure it's a bug or feature from Rails (I use Rails 6.1.1).
For a quick fix, you can change #todo_list.todo_items.new to TodoItem.new.

I'm getting error "uninitialized constant SongsController" on ruby on rails

I'm getting an error like that, I'm new in rails.
my routes.rb
Rails.application.routes.draw do
get 'welcome' => 'page#search'
resources :songs
end
search.html.erb
<%=form_for #song do |f|%>
<%=f.text_field :search%>
<%=f.submit 'Search'%>
<%end%>
page_controller.rb
class PageController < ApplicationController
attr_accessor :a
def search
#songs = Song.all
#song = Song.new
end
def new
#song = Song.new
end
def Create
#song = Song.new()
if #song.save
rediect_to ''
else
render new
end
end
def parameter
params.require(#song)
end
end
path
welcome_path GET /welcome(.:format) page#search
song_new_path POST /song/new(.:format) song#new
songs_path GET /songs(.:format) songs#index
POST /songs(.:format) songs#create
new_song_path GET /songs/new(.:format) songs#new
edit_song_path GET /songs/:id/edit(.:format) songs#edit
song_path GET /songs/:id(.:format) songs#show
PATCH /songs/:id(.:format) songs#update
PUT /songs/:id(.:format) songs#update
DELETE /songs/:id(.:format) songs#destroy
you declared resources :songs in your routes file, so Rails expecte SongsController inheriting from ApplicationController in your app/controllers folder. If you do not have this controller, create new file:
app/controller/songs_controller.rb
class SongsController < ApplicationController
# add implementation of CRUD methods
def index
end
def show
end
def new
end
# ...
end
Rename your controller as SongsController

Call rendering from another controller

I have a rails application with 3 controllers :
categories, subcategories, all
The two firsts fetch data from the db and render it via
render "list"
The last one should get the result of the first controller, the result of the second one and render them.
Simplified example :
# categories_controller.rb
class CategoriesController < ApplicationController
def index
render "list"
end
end
# subcategories_controller.rb
class SubcategoriesController < ApplicationController
def index
render "list"
end
end
# all_controller.rb
class AllController < ApplicationController
def index
# should render the both and join them
end
end
# list.html.erb
this is the list
Result :
/category
=> "this is the list "
/subcategory
=> "this is the list "
/all
=> "this is the list this is the list "
I tried to call
render "subcategory"
render "subcategory/index"
But nothing seems to work.
Do you have an idea?
Thanks,
A previous approach was the following :
# categories_controller.rb
class CategoriesController < ApplicationController
def self.getAll
return Category.all
end
def index
#data = CategoriesController.getAll
# some logic here
render "list"
end
end
# subcategories_controller.rb
class SubcategoriesController < ApplicationController
def self.getAll
return Category.all
end
def index
#data = SubcategoriesController.getAll
# some logic here
render "list"
end
end
# all_controller.rb
class AllController < ApplicationController
def index
#categoriesData = CategoriesController.getAll
#subcategoriesData = SubcategoriesController.getAll
render 'all'
end
end
# list.html.erb
<%= #data %>
# all.html.erb
<%= #categoriesData %>
<%= #subcategoriesData %>
But I have to rewrite a part of logic that is already there ...
You need to explicitly build the output that you need in the all controller action, rather than trying to join the outputs of other actions.
Two approaches:
Retrieve the items you want with two database calls, join them together into one big set of list items, then render the list as in the other actions.
class AllController < ApplicationController
def index
categories = Categories.all
sub_categories = Subcategories.all
#all = categories + sub_categories
end
end
Retrieve both sets of data, make the list.html page call a partial called _sub_list.html or something, then have a new template for the all page called all.html, which renders the new partial twice, once for each set of data.
class AllController < ApplicationController
def index
#categories = Categories.all
#sub_categories = Subcategories.all
end
end
To reuse logic across controllers. use a concern:
module SharedLogic
extend ActiveSupport::Concern
def useful_function
# some code...
end
end
class SubcategoriesController < ApplicationController
include SharedLogic
def index
#data = SubcategoriesController.getAll
# some logic here
useful_function
render "list"
end
end

Instance variable from Rails model: undefined method `any?'

Trying to display some affiliate products while keeping my controllers as skinny as possible. But does anybody know why this isn't working?
undefined method `any?' for nil:NilClass
app/models/shopsense.rb
require "rest_client"
module Shopsense
def self.fetch
response = RestClient::Request.execute(
:method => :get,
:url => "http://api.shopstyle.com/api/v2/products?pid=uid7849-6112293-28&fts=women&offset=0&limit=10"
)
# !!! ATTENTION !!!
#
# It works if I put the below in `shopsense` in the controller instead
#
#products = JSON.parse(response)["products"].map do |product|
product = OpenStruct.new(product)
product
end
end
end
app/controllers/main_controller.rb
class MainController < ApplicationController
before_action :shopsense
def index
end
def shopsense
Shopsense.fetch
end
end
app/views/main/index.html.erb
<% if #products.any? %>
<% #products.each do |product| %>
<div class="product">
<%= link_to product.name %>
</div>
<% end %>
<% end %>
Your index.html.erb is requesting an instance variable #products, which isn't available through in the index action of your controller.
Put the instance variable in your index action:
def index
#products = Shopsense.fetch
end
Instance variables don't belong in a model. So you can not use #products there. Put it back into the controller and you are fine.
Correct - instance variables in rails declared in the controller are available in the view. In your case, you are declaring the instance variable inside a module, and not the controller.
Try this:
def index
#products = shopsense
end
In this case, your controller will pass on the #products instance variable to the view
Because #products should be a member of MainController to be visible inside the view.
This should work:
class MainController < ApplicationController
before_action :shopsense
...
def shopsense
#products = Shopsense.fetch
end
end
Another option is to include the Shopsense module into MainController:
module Shopsense
def fetch
...
end
end
class MainController < ApplicationController
include Shopsense
before_action :fetch
...
end

UrlGenerationError in Stores#index

I'm running into what I think is a routing error. I was creating a shopping cart but somehow I'm missing the required key [:product_id]. I believe it may be beginners mistake not placing the right code somewhere.
No route matches {:action=>"add", :controller=>"carts", :product_id=>nil} missing required keys: [:product_id]
app/views/stores/index.html.erb:34:in block in _app_views_stores_index_html_erb__2030225986820803304_70197415655620
app/views/stores/index.html.erb:3:in _app_views_stores_index_html_erb__2030225986820803304_70197415655620
Carts_Controller.rb
class CartsController < ApplicationController
def show
cart_ids = $redis.smembers current_user_cart
#cart_products = Product.find(cart_ids)
end
def add
$redis.sadd current_user_cart, params[:product_id]
render json: current_user.cart_count, status: 200
end
def remove
$redis.srem current_user_cart, params[:product_id]
render json: current_user.cart_count, status: 200
end
private
def current_user_cart
"cart#{current_user.id}"
end
def carts_params
params.require(:cart).permit(:product_id)
end
end
Cart.rb
class Cart < ActiveRecord::Base
belongs_to :user
end
Products_Controller.rb
class ProductsController < ApplicationController
def index
#products = Product.all
#order_item = current_order.order_items.new
end
end
Routes.rb
resource :cart, only: [:show] do
put 'add/:product_id', to: 'carts#add', as: :add_to
put 'remove/:product_id', to: 'carts#remove', as: :remove_from
end
Stores/index.html
<%=link_to "", class: "button", data: {target: #cart_action, addUrl: add_to_cart_path(#product_id), removeUrl: remove_from_cart_path(#product_id)} do%>
<%=#cart_action%>
When you are doing add_to_cart_path(#product_id) it sends the param as :id and you want it to be :product_id.
You can fix this two ways.
Way #1
In your button/link_to change
add_to_cart_path(#product_id)
to:
add_to_cart_path(product_id: #product_id)
Way #2
You can update your controller and route to expect and use the param id instead of product_id

Resources