How to display latest posted? - ruby-on-rails

So, I have an app that allows users to upload songs and vote them up. The songs with the higher vote count end up on top, and the newly posted songs need to be voted up to be seen (think hackernews).
I also have a 'new songs' page that I'd like to display the newly uploaded songs first negating the votes (alas hackernews)
My current song_controller sorts the songs in the index as such:
def index
#songs = Song.order('plusminus')
end
I have a def new_songs action in the song_controller but I'm not sure how to have it display just the new songs and bypass the thumbs up gem voting.

I don't know much about that gem, but it seems to be scope based. How about just querying the data normally?
def new_songs
#songs = Song.order "id DESC"
end
or better, write your own scope:
# song.rb
scope :newest, order("id DESC")
# song_controller.rb
def new_songs
#songs = Song.newest
end

Pass an instance variable containing the most recently uploaded songs from your controller action to the view:
# app/controllers/songs_controller.rb
def index
#songs = Song.order('plusminus')
#newest_songs = Song.order('created_at DESC').limit(10) # returns the ten most recently uploaded songs
end
In the view, you'll have access to the ten most newest songs via the #newest_songs instance variable:
# app/views/songs/index.html.erb
<h1>Highest Voted Songs</h1>
<% #songs.each do |song| %>
# view logic
<% end %>
<h1>Newest Songs</h1>
<% #newest_songs.each do |song| %>
# view logic
<% end %>
Alternatively, if you want to display the newest songs via an entirely separate view, you could do something akin to the following:
# app/controllers/songs_controller.rb
def new_songs
#songs = Song.order('created_at DESC')
end
# app/views/songs/new_songs.html.erb
<h1>Newest Songs</h1>
<% #newest_songs.each do |song| %>
# view logic
<% end %>
# config/routes.rb
resources :songs do
collection do
get 'new_songs' # creates route from `songs/new_songs` to the `songs#new_songs` controller action
end
end

Related

Rails: How to review content before submit/save?

I know it is simple but I can't get my head around a solution.
It is a job board site. Lets say it's functionality similar to this site. When a user fill all required information and click "To next step" or "Preview", another page loads with all filled data. That page is similar to the final page when data is saved.
When user on preview page, it can go forward and submit the page (in this case it will be saved to DB). Or, click back to Edit the job.
I tried the following::
Within _form.html.erb I added a preview button
<%= f.submit "Preview", :name => 'preview' %>
Within JobControllers I altered create method
def create
if params[:preview]
#job = Job.new(jobs_params)
render 'jobs/preview'
else
#job.save
end
end
Created a Preview view /jobs/preview.html.erb
Now I have 2 problems.
1- Within my preview page, I have an edit button like so: <%= link_to "Edit Job", edit_job_path(#job) %>. But I have an error because I can't find #job. Error says: No route matches {:action=>"edit", :controller=>"jobs", :id=>nil} missing required keys: [:id]
SOLUTION Changed like to <%= link_to 'Back to edit', 'javascript:history.go(-1);' %>
2- How I would submit and add to my DB all information on preview page?
Thank you.
Once I've given a similar task. What I've done is to save records, but not to publish. In my index (resource listing) action of relevant controller, I only fetch published records. Also show action prechecks if that record's published attribute is set to true.
What was my model/controllers looked like before
#model
class Book < ActiveRecord::Base
...
scope :active, -> { where(published: true).some_other_queries }
self.active?
(published && some_other_requirements)
end
...
end
#controller
def index
#books = Book.active
...
end
def show
if #book.active?
render 'show'
...
else
...
end
end
First added a secret key for previews.
#model
def secret
#some custom random key generation
# e.g. Digest::MD5.hexdigest("#{id}_#{ENV['RAILS_SECRET']}")
end
Then added preview action to controller
def preview
# i don't check if the record is active.
# also added a security layer, to prevent irrelevant guys to view
# that record
if #book.secret == params[:secret]
render 'show'
else
...
end
end
In dashboard
...
= link_to "Preview", preview_book_path(book, secret: book.secret)
...
then added a member route
#routes
resources :books do
get :preview, on: :member
end
When I have to do something like this what I normally do is create a review table in my app. This table looks just like the table that is going to saving to.
When they press the "Approved" or "Save" button just populate the new table with the proper data.
I like to create a routes to handle this
resources :something do
match 'move_to_something_else' => 'somethings#move_to_something_else', as: :move_to_something_else, via: :all
end
Now on the controller we can do the following:
def move_to_something_else
#something = Something.find(params[:id])
#something_else = SomethingElse.new
#something_else.name = #something.name
....
#something_else.save
redirect_to something_else_path(#something_else)
end
Alternative you could add a state to your table with the default value of 'draft'
# config/routes.rb
resources :something do
match 'published' => 'somethings#published', as: :published, via: :all
end
# Controller
def published
#something = Something.find(params[:id])
#something.state = 'published'
#something.save
redirect_to something_path(#something)
end

How to link model to controller action

I have a Slider model in my project and it has a lot of polymorphic associations with other model like Product, Manufacturer, Article and etc.
So, when I use 'show' action with one of the models I also show related Slider. It's ok. But sometimes I need to show Slider with 'index' action.
What is the best way to link some of the sliders to actions, not to other models?
UPDATE
routes
resources :products, :articles, :industries, :manufacturers, only: [:index, :show]
Product controller
class ProductsController < ApplicationController
load_resource
# GET /products
# GET /products.json
def index
#catalog = Product.by_type_and_manufacturer
end
# GET /products/1
# GET /products/1.json
def show
#page_slider = #product.slider
end
end
So in 'show' action I just use product.slider to get related Slider instance. But I want to show another slider for all products by index action.
In that case, what you're trying to do is not possible. You cannot create a relation to a controller action. What you need to do is link the relation's controller action, rather than trying to create a relation to the controller action. A model can only be related to another model (you cannot has_many index, show, delete, etc...)- In other words, call up the data for the relation, and link to that relation's controller action in the view.
example:
#Models:
class Page < ActiveRecord::Base
has_many :sliders
end
class Slider < ActiveRecord::Base
belongs_to :page
end
#Controllers
class PagesController < ApplicationController
def index
#pages = Page.all # lists all pages
end
def show
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
end
# if you would like to show a page that just has all sliders for a specific page and not the page itself...
def show_page_sliders # you will have to create a route and view for this manually
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
# note that this controller action is identical to the show action, because the data we're going to need is the same- the difference comes in the view
end
end
class SlidersController < ApplicationController
def index
#sliders = Slider.all
end
def show
#slider = Slider.find(params[:id])
end
end
# Views
# page#index
<% #pages.each do |p| %>
...
page listing code goes here. if you want to list the sliders for each page on the index...
<% p.sliders.each do |s| %>
...
individual slider info goes here
...
<% end %>
...
<% end %>
# pages#show
<%= #page.name %>
<%= #page.content %> <!-- or whatever data you have for page -->
# since here we are showing a singular page, we can just use our #page instance variable to list out the sliders
<% #page.sliders do |s| %>
...
Slider listing code goes here
...
<% end %>
# pages#show_sliders
<!-- this is identical to the page#show view, minus the actual page info, and with the addition of a link back to the parent page -->
<%= link_to "Back to page", page(s.page_id) %>
<% #page.sliders do |s| %>
...
Slider listing code goes here
<!-- you can link to any path from the slider listing -->
<%= link_to "Show", slider(s.id) %>
<%= link_to "Edit", edit_slider_path(s.id) %>
<%= link_to "Delete", delete_slider_path(s.id) %>
...
<% end %>
#######################UPDATE#############################
# to define one slider per controller action
class PagesController < ApplicationController
def index
#pages = Page.all
# you need to add a "controller_action" column to your Slider model
#slider = Slider.find_where(controller_action: "pages#index")
end
def show
#page = Page.find(params[:id])
#slider = Slider.find_where(controller_action: "pages#show")
end
# etc ...

Ruby on Rails 4, How to Display User in Another page

I am new to Ruby on rails. I've created basic demo apps by tutorial learning by examples.
Application have three model User,Village and article
Village has many users, Village has many articles, user and article belongs to village
I work , migration work fine
when iam in http://0.0.0.0:3000/villages/1 , i display all user that belong to village 1
My question is how display in all user in village one this url http://0.0.0.0:3000/villages/1/users
To do that you need to add the url to the routes.rb file under the config folder.
Add line like below
resources :villages do
member do
get '/user', to: 'villages#show'
end
end
I am assuming that your villages show action is the one that has all the user details displayed.
routes.rb
resources :villages do
member do
get :users, :articles
end
end
In villages_controller
def users
#village = Village.find(params[:id])
#values = #village.users.paginate(page: params[:page])
render 'show_data'
end
def articles
#village = Village.find(params[:id])
#values = #village.articles.paginate(page: params[:page])
render 'show_data'
end
In show_data.html.erb
<% if #values.any? %>
<% #values.each do |value| %>
<%= value.name %>
<% end %>
<%end%>
<%= will_paginate %>

Displaying Associated Objects in Rails Views

I am working on a rails 4 application that currently has two models User and Status. In the user model I defined the association below. Both the status and user tables are populating with information. Statuses are loading with an associated user_id
User Model
class Status < ActiveRecord::Base
belongs_to :user
end
I have the following block in my show status view which will display the user_id and and the content of the status
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user_id%></strong>
<p> <%=status.content%></p>
I would like to display the user's first name instead. According the tutorial i'm taking I should be able to use this code since I have the association defined however it's returning the error below.
<%=#status.user.first_name%>
Error
#==>undefined method `first_name' for nil:NilClass
How can I display first_name in the controller? Do I need to define a new method for user or should the association provide?
Relevant Controller Code for Reference
class StatusesController < ApplicationController
before_action :set_status,:set_user, only: [:show, :edit, :update, :destroy]
# GET /statuses
# GET /statuses.json
def index
#statuses = Status.all
end
# GET /statuses/1
# GET /statuses/1.json
def show
puts "debug msg #{#status.inspect}"
end
# GET /statuses/new
def new
#status = Status.new
end
# GET /statuses/1/edit
def edit
end
# POST /statuses
# POST /statuses.json
...
...
...
private
# Use callbacks to share common setup or constraints between actions.
def set_status
#status = Status.find(params[:id])
puts "in set status"
end
def set_user
#status.user = User.find_by(#status.user_id)
end
# Never trust parameters from the scary internet, only allow the white list through.
def status_params
params.require(:status).permit(:content, :user_id)
end
end
Sees like there is no problem in your code. The error undefined method first_name for nil:NilClass means that the status object not associated with user or user have no field first_name. Try following code:
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user.try(:first_name) %></strong>
<p> <%=status.content%></p>
I am not sure what page you are trying to display <%=#status.user.first_name%> this on, but this should work.
You can use the will_paginate gem:
def show
#statuses = #statuses.paginate(page: params[:page])
end
add this to the view:
<%= will_paginate %>
or this should be the normal way:
def show
#statuses = #statuses.find(params[:id])
end

Does accessing the database in views via view-models or view-helpers break the MVC paradigm?

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/

Resources