Rails Craiglist like list of items - ruby-on-rails

So I have a state model and city model associated with has_many and belongs_to. I want to display a page with each state with its associated cities underneath.
I created a page controller and page called "Locations" and manually entered in
<%= link_to "Allentown", allentown_path %>
which then takes you to the allentown page.
On the allentown page I filtered the listings by adding this code to the pages controller
def allentown
#title = "Allentown Listings"
#tattoo_briefs = TattooBrief.where( :city_id => 1 ).find(:all, :order => "id DESC" )
end
I know this isn't DRY. Also can get very cumberson if I have 200 cities. Is there a better way?

You need to add a resource to your routes:
routes.rb
resources :city
That essentially gives you all the RESTful actions for the City model. Then, in your controller, use the show action to..wait for it..show your city page
cities_controller.rb
def show
#city = City.find(params[:id])
#title = "#{#city.name} Listings"
#tattoo_briefs = TattooBrief.where( :city_id => params[:id] ).find(:all, :order => "id DESC" )
end
you can still modify this by studying more on routes and controllers from the rails api. With added knowledge, you can get to allentown by modifying your route to use the city name instead of the id.

Related

Rails association - what controller code gets executed?

I'm trying to make a change to some controller code. But, I don't understand where to put it.
I have the following in a new Contact form:
<%= f.association :location, :label_method => :name, :label => 'Location:' %>
I assumed that code would execute the index code in the location's controller.
But, I just deleted all of the code in the location index and the Contact form with the association to Location still has data in it.
I want the following code to execute at the Contact association stmt, but I don't know where to put it:
#locations = Location.ordered_by_ancestry_and(:name).map { |l| [" " * l.depth + l.name, l.id] }
UPDATE1
This is the development.log
Processing by ContactsController#new as HTML
Location Load (0.2ms) SELECT "locations".* FROM "locations" ORDER BY (case when locations.ancestry is null then 0 else 1 end), locations.ancestry, name
UPDATE2
I changed the ContactsController#new to this for testing:
# GET /contacts/new
# GET /contacts/new.json
def new
#locations = Location.first
And I still got all the locations in the select box.
The SimpleFormFor association helper method will generate a list (as a select list by default, but can be modified to radio or checkbox list) that acts as a nested attribute for that association. When submitted this field will be passed to the same controller action as the parent form - in this case the ContactsController#create action.
As far as your customized list of locations, you can pass this as a collection option to the association method:
<%= f.association :location, :collection => #locations, :label_method => :name, :label => 'Location:' %>
The actual place to build this #locations list would be in any action that may need to access it - that includes new and edit, as well as create and update (in case an error prevents your form from submitting). You can use a before_filter to eliminate any duplication.
The code may look something like:
class ContactsController < ApplicationController
before_filter :load_locations, :only => [:new, :edit, :create, :update]
#... actions go here
private
def load_locations
#locations = Location.ordered_by_ancestry_and(:name).map do |l|
[" " * l.depth + l.name, l.id]
end
end
end

Rails 3, change or fill submitted form values in controller

I have two independent "component" models, contacts and articles. Contacts come in many types (producers, exporters etc...).
In the new article form I have a dropdown selector with contacts (id and title) and want to store the selected value and text in the article table.
In the new article form view:
<%= f.select :producer_id, options_for_select(producers, #article.producer_id) %>
That works and producer_id is stored in article table.
That's clear and logical to me, but in some cases I also need to store the selected contact's title in producer_title.
I have read many different options like "do it in model, before save", or "do it in controller", and I have done it inside controller.
Article controller (only part from update):
#cont_name is producer title from Contacts
def update
params[:article][:producer_title] = Contact.where(id: params[:article][:producer_id]).pluck(:cont_name).first
end
This works, but is it the best-practices approach to this problem?
Also, why I can't get it to work if I change the params[producer_id] part to use: id: params[:producer_id] ?
Best regards and thanks.
How about something like the following instead:
def edit
#article = Article.find(params[:id])
#producers = Contact.where(type: "producer") # or however you distinguish between producers and other contacts
end
Then in your form change it to:
f.select :producer_id, options_from_collection_for_select(#producers, "cont_name")
# you might need to do (#producers, "cont_name", "cont_name"), can't quite remember
Then your update action will be much simpler:
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
...
else
...
end
end
The reason :id => params[:producer_id] doesn't work is that params is a nested hash, so something like:
params = { :article => { :producer_id => 34, :title => "Cool Article" }, :another_key => { :key => :value } }
So to access the producer_id you first have to retrieve the article hash, otherwise it will only look through the first set of keys which include article and another_key in the example above, but don't include producer_id.

Dynamic url_path in each loop

I have a city page with a jQuery content carousel. The content of the carousel is filed by a each loop.
CityController
#events = #city.events.find_all_by_hot(true)
#activities = #city.activities.find_all_by_hot(true)
#sights = #city.sights.find_all_by_hot(true)
#hot = #events + #activities + #sights
Class city
has_many: events
end
class events
belongs_to :city
has_many :attachments, :as => :attachable
accepts_nested_attributes_for :attachments
end
Activities and sights models are the same
City view content slider:
#hot.each do |a|
a.attachments.each do |a|
= image_tag(a.file.url, :height =>"325px", :width =>"650px" ), url_path
I want to generate links (url_path) in my each loop...how can I realize this? It cannot place the url_path of the routes because they are dymanic based on which attachment (image) is loaded.
Although your syntax of image_tag is incorrect you can try this
#hot.each do |hot|
hot.attachments.each do |a|
link_to polymorphic_path(a.attachable) do
image_tag(a.file.url, :height => "325px", :width => "650px")
end
end
end
If I understand your problem correctly. Also check out the polymorphic_path helper, which is what you need.
Am I right that the link should point to the a which can be any of events, activities, sights? as
#hot = #events + #activities + #sights
I would try creating a special controller action in the CityController
def hottie
#duck = Kernel.const_get(params[:type]).find_by_id(params[:id])
redirect_to #duck
end
then add something like
match 'hottie/:type/:id' => 'city#hottie', as: 'hot'
which should give you a path helper that you can use as this:
<%=link_to("Open", hot_path(a.class.to_s, a.id)) %>
Addition: this is of course a bit dirty and needs some security things considered (e.g. limit it to show only special types). You could also consider moving the three classes Event, Activities and Sights into a Object Hierarchy using STI; that should eliminate the need of passing the type in the request.

Passing IDs through a new Form

This question is about a different approach I'm trying to the one asked here:
Passing IDs through a new Form
I have a group#view page, that is accessed by a Person. In this page, the Person can see the members of the group via methods I developed. The problem is that I need to create the model Honors using the Id from the group, the id from the person accessing the page, and the id from a member of this group.
In my Honors controller I have:
def create
#person = Person.find(current_person)
#asked_groupmembership = #person.owned_group_memberships.find_all_by_status(true,:include => [:group, :member])
#asked_groupmembership.each do |agm|
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(current_person), :honored => Person.find(agm.member.id))
end
if #honor.save
...
end
In my view I have a link that directs the person to the form in order to create a new honor:
<% #asked_groupmembership.each do |agm| %>
<%= link_to "Create Honor", new_honor_path(:group_id => #group.id, :person => current_person.id,
:honored => agm.member.id) %>
But in my forms I can't get the ids and stuff
<% form_for(:honor, :url => honors_path(:group_id, :person,
:honored)) do |f| %>
The error I get is that I can't find Group without an Id.
Any ideas? Thanks.
##Edited2##
Changed my crontroller
def new
##person = Person.find(params[:person])
##honored = Person.find(params[:honored])
##group = Group.find(params[:group_id])
#honor = Honor.new
end
def create
#person = Person.find(current_person)
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(params[:person]),
:honored => Person.find(params[:honored]))
if #honor.save
...
end
First, it seems like you are missing a controller method. Along with every form that creates a new object there are typically two controller methods
new
Gathers up any data that the form needs to render itself
Renders the form
create
Collects the data from the form
Creates the new object
It looks to me like you are missing the new method. In the new method you would gather up all the hidden data that the form needs (e.g. the information that the user is not going to type in directly, like the #person info). Then, I would put this information in your form using hidden form parameters (rather then trying to put it in the form URL).
Objective: From the group#view, loop through and create and "Add Honor" link for each member of the group, while keeping track of the specific member being honored, group in which said member is honored, and the person who is honoring.
The following accomplishes that objective.
Route:
match "honors/:group/:person/:honored" => "honors#create", :as=>"my_custom_new_honor"
Group view:
<% #asked_groupmembership.each do |agm| %>
<%= link_to "Create Honor", my_custom_new_honor_path(:group=> #group.id, :person => current_person.id,:honored => agm.member.id) %>
Honor Controller, Create Method
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(params[:person]),
:honored => Person.find(params[:honored]))
In this scenario you do not need the honor#new method, you're going directly to the create method. This assumes all the relationships are established correctly.

Creating a new nested resource

I'm working on a basic garden logging application which consists of gardens, plants and planted plants. Each user has one or many gardens, plants are master definitions, and a planted plant can be thought of as an instance of a plant in a specific user's garden.
In my routes.rb file I have the following:
map.resources :gardens do |gardens|
gardens.resources :planted_plants, :has_many => :plant_log_entries, :collection => { :filter => :post, :choose_garden => :post}
gardens.resources :garden_log_entries
end
map.resources :plants
This makes sense to me when retrieving a list of planted_plants in a user's garden, but I'd like to create a planted_plant record from the index of plant. The problem is, a user can have multiple gardens. How can I create a new form for a planted_plant that allows the user to specify which garden should be used?
The current route requires a garden_id - which makes sense for retrieval, but I'd like to supply that as a parameter for creation.
Thanks in advance for any help!
I would add another (non-nested) route, to allow access to the PlantedPlantsController#create action without specifying the garden_id in the URL:
map.resources :planted_plants, :only => :create
This will allow you to post your form to /planted_plants. As for the form itself, you'll probably need something like this:
<% form_for #planted_plant do |p| %>
<%=p.label "garden_id", "Garden" %>
<%=p.collection_select :garden_id, current_user.gardens, :id, :name %>
... other fields ...
<% end %>

Resources