I have two controllers like this:
app/controllers/collection_controller.rb:
class CollectionController < ApplicationController
def create
#collection = Collection.new(name: params[:name])
#collection.save!
render #collection
end
end
And an inherited class:
app/controllers/enterprise/collection_controller.rb:
class Enterprise::CollectionController < ::CollectionController
def create
#collection = Collection.new(name: params[:name])
#collection.company = Company.find(params[:company])
#collection.save!
render #collection
end
end
I have two partials:
app/view/collections/_collection.json.jbuilder:
json.extract! collection, :title, :description
json.users do
json.partial! collection.user
end
app/view/collections/_user.json.jbuilder:
json.extract! user, :name, :surname
The problem is:
When I load Enterprise::CollectionController#create, I get missing template app/views/enterprise/collections/_collection ....
I want Enterprise::CollectionController to use app/view/collections/_collection.json.jbuilder instead of app/view/enterprise/collections/_collection.json.jbuilder.
I tried to do something like:
render #collection, partial: 'collections/collection', but I receive:
But I receive:
missing template for ... app/views/enterprise/users/_user ...
How can I solve this?
After you changed your render partial to
render #collection, partial: 'collections/collection'
you are not getting an error for collection partial. you are getting an error for user partial. You will have to change the way you are rendering user partial to
json.partial! "collections/user", user: collection.user
Update:
you can try append_view_path. So basically you will append to the default search locations
class Enterprise::CollectionController < ::CollectionController
before_filter :append_view_paths
def append_view_paths
append_view_path "app/views/collections"
end
end
So rails will search in app/views/enterprise/collections, app/views/shared, app/views/collections in order
You can also use prepend_view_path if you want rails to search in app/views/collections first
PS: I haven't tested this.
Related
I want my trade page to show a list of every Item that a user has added to their cart, and I'm having trouble understanding why this implementation is giving me a NoMethodError
So in the #show action of my TradesController I have a trade_ids variable that contains an array of added items returned by $redis.smembers current_user_trade. I then use this to perform a lookup on the id of each item, and loop through the instance variable in my view.
My Trades Controller:
class TradesController < ApplicationController
def show
trade_ids = $redis.smembers current_user_trade
#trade_items = Item.find(trade_ids)
end
def add
$redis.sadd current_user_trade, params[:item_id]
render json: current_user.trade_count, status: 200
end
def remove
$redis.srem current_user_trade, params[:item_id]
render json: current_user.trade_count, status: 200
end
private
def current_user_trade
"trade#{current_user.id}"
end
end
Here's the method I'm using to add items to current_user_trade:
class Item < ActiveRecord::Base
extend FriendlyId
friendly_id :slug, use: [:slugged, :finders]
def slug
[artist.parameterize, title.parameterize].join("-")
end
def trade_action(current_user_id)
if $redis.sismember "trade#{current_user_id}", id
"Remove from"
else
"Add to"
end
end
end
My routes:
resource :trade, only: [:show] do
put 'add/:item_id', to: 'trades#add', as: :add_to
put 'remove/:item_id', to: 'trades#remove', as: :remove_from
end
And then in my trades view I've got a basic:
<% #trade_items.each do |item| %>
<p><%= item.title %></p>
etc
<% end %>
My initial thought is that this had something to do with the fact that I've been using FriendlyId for slug generation. But according to FriendlyId's doc, adding :finders to the slug declaration in my Item model should reenable lookups by id. But it's not.
I also tried refactoring this so that it used my Items :slug, but that too was to no avail.
Fixed it. Turns out it was cached. I switched from storing item_id to item_slug halfway through, and needed to run redis-cli flushall to get it to store and access the right data.
So as it stands I have a form partial which starts off as:
<%= form_for(#merchandise) do |f| %>
It works perfectly for editing the data that I have already assigned in the rails console. When I try to use it for a "new" form that creates new merchandise (in this case the singular form of merchandise, I don't have nested resources, multiple models etc.), I get a no Method error that states
"undefined method 'merchandises_path' for #<#<Class:0x64eaef0>:0x95d2370>".
When I explicitly state the URL in the form_for method:
<%= form_for(#merchandise url:new_merchandise_path) do |f| %>
I get it to open and I finally have access to the form, however in this case I get a routing error that states
"No route matches [POST] "merchandise/new"".
I decided to write out that route in my routes file which previously just had:
root "merchandise#index" resources :merchandise
After I add the route it literally does nothing. I click submit and it takes me to the form but there is no new data in the database. I am at a complete loss and have been at this for hours googling and stack overflowing and I just don't know what to do anymore. All help is seriously appreciated. I'm adding a pastebin with all my code in the following url:
http://pastebin.com/HDJdTMDt
I have two options for you to fix it:
Option 1:
Please try to do this for best practice in Rails:
routes.rb
change your routes use plural
resources :merchandises
merchandises_controller.rb
Rename your file controller and class into MerchandisesController
class MerchandisesController < ApplicationController
def index
#merchandise = Merchandise.all
end
def new
#merchandise = Merchandise.new
end
def create
#merchandise = Merchandise.new(merchandise_params)
if #merchandise.save
redirect_to merchandises_path
else
render :new
end
end
def show
#merchandise = Merchandise.find(params[:id])
end
def edit
#merchandise = Merchandise.find(params[:id])
end
def update
#merchandise = Merchandise.find(params[:id])
if #merchandise.update(merchandise_params)
redirect_to #merchandise, notice: "The movie was updated"
else
render :edit
end
end
def merchandise_params
params.require(:merchandise).permit(:shipper, :cosignee, :country_arrival_date, :warehouse_arrival_date, :carrier, :tracking_number, :pieces, :palets, :width, :height, :length, :weight, :description, :cargo_location, :tally_number, :customs_ref_number, :released_date, :updated_by, :country_shipped_to, :country_shipped_from)
end
end
Option 2:
If you want to not change many code
/merchandise/_form.html.erb
in partial file
/merchandise/new.html.erb
<%= render 'form', url: merchandise_path, method: 'post' %>
/merchandise/edit.html.erb
<%= render 'form', url: category_path(#merchendise.id), method: 'put' %>
I'm just started with rails and until now I was very happy with it, but there is one thing I can't figure out.
I have some ActiveRecords models in a namespace "Monitor", and I have some controllers in a Namespace "Settings". What I want to accomplish is that I can use the namespaced models in my settings controllers/forms.
I've got this:
/config/routes.rb
namespace :settings do
resources :queues, :channels
end
/app/controllers/settings/queus_controller.rb
class Settings::QueuesController < ApplicationController
def new
#queue = Monitor::Queue.new()
render 'form', :layout => false
end
def create
#queue = Monitor::Queue.new(post_params)
if (#queue.save)
#status = 'added'
render 'success'
else
render 'form', :layout => false
end
end
def edit
#queue = Monitor::Queue.find(params[:id])
render 'form', :layout => false
end
...
end
/app/models/monitor/queue.rb
module Monitor
class Queue < ActiveRecord::Base
end
end
/app/views/settings/form.html.erb
<%= form_for #queue do |f| %>
...
<% end %>
Now Rails is complaining about a missing method : monitor_queues_path or Rails generates a path like /monitor/queues instead of /settings/queues(:new/edit).
What am I missing here?
Aah I found it!
This post gave me the proper solution:
Rails namescoped model object without module prefix route path
The problem came from the prefix from the ActiveRecord class:
module Monitor
class Queue < ActiveRecord::Base
end
end
This should be
module Monitor
class Queue < ActiveRecord::Base
def self.model_name
ActiveModel::Name.new("Monitor::Queue", nil, "Queue")
end
end
end
After changing this I only needed to change the form_for in the correct way:
<%= form_for [:settings, #queue] do |f| %>
And that fixes it :D
You are using nesting for your Queue models. Therefore your form_for call needs to know about the parent model too. So in your case you nested Queue under Setting so you will need to provide a setting object as well. I'm guessing in your controller you made a #setting variable. If this is the case then the following code will work for you.
<%= form_for [#setting, #queue] do |f| %>
<%# Your form code here -%>
<% end -%>
I found a solution from my friend #mkhairi he said to use this on the parent model :
class YourParentModel < ApplicationRecord
def self.use_relative_model_naming?
true
end
end
then you can use back ur lovely short path.
Source :
https://coderwall.com/p/heed_q/rails-routing-and-namespaced-models
In my Rails app I already have the following code:
<% %w(number_of_students edit_class_name tech_help).each do |modal| %>
<%= render "common/modals/#{modal}" %>
<% end %>
There will be a few more modals added into app/views/common/modals and instead of explicitly listing them out in the %w() I was wanting to loop through the common/modals directory and just render each file.
Here is what I came up with:
def render_modals
files = Dir.glob("#{Rails.root}/app/views/common/modals/*").collect { |file| File.basename(file, ".html.erb").sub("_", "") }.flatten
files.collect do |modal|
render partial: "common/modals/#{modal}"
end.join.html_safe
end
define a simple method in where is more appropriate (maybe app helper?) like this:
def modals
%w(number_of_students edit_class_name tech_help)
end
if you need these modals in a controller/model too, maybe you should define this method in an appropriate class? For example
class Modal
def self.types
%w(number_of_students edit_class_name tech_help)
end
end
Also, if you are rendering the templates often, then also define
def render_modals
modals.map do |modal| # Modals here should be the method that you just defined, example, Modal.types
render partial: "common/modals/#{modal}"
end.join
end
I believe all of the following break the MVC paradigm but wanted to double check if this was the case. In all cases the view is directly accessing data rather than having the data being passed in. From my understanding of MVC, it should never do that. The controller should get all the data that is necessary to render the view as to not couple the view and model directly. Is my understanding correct?
Accessing the database through a view helper
# in app/helpers/view_helper.hrb
def some_view_helper(person_id)
#person = Person.find(person_id)
end
Accessing another web server through a view helper
# in app/helpers/view_helper.hrb
def another_view_helper(person_id)
# makes http request over the wire to get json back
#json = WebService.get_person(person_id)
end
Accessing the database through a view model
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id])
#page_model = PageModel.new(#person)
end
#in app/views/persons/show.html.erb
<% #page_model.friends.each do |friend| %>
...
<% end %>
#in app/models/person.rb
class Person < ActiveRecord::Base
has_many :friends
end
#in app/models/page_models/page_model.rb
def initialize(person)
#person = person
end
def friends
#person.friends
end
Accessing web server to get data through a view model
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id])
#page_model = PageModel.new(#person)
end
#in app/views/persons/show.html.erb
<% #page_model.friends.each do |friend| %>
...
<% end %>
#in app/models/page_models/page_model.rb
def initialize(person)
#person = person
end
def friends
WebService.get_friends_for_person(person_id)
end
For 1 and 2, you could just set an instance variable (#person) in the controller.
For 3, your view code isn't so bad, but why have a separate page model? You can also load the friends up front in the controller:
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id], :include => :friends)
#friends = #person.friends
end
Example 4 is a bit worse, since you're doing external web service calls in a view. Don't do that.
This article has a good example of what an ideal clean view would look like: http://warpspire.com/posts/mustache-style-erb/