How to create multiple index endpoints for one Rails controller - ruby-on-rails

this seems like a simple question but I can't find the answer. I have books in my Rails app, and a scope for featured books. I want an endpoint that lists all books, and an endpoint that lists just the featured books.
I have one API Books Controller with one index action, so site.com/api/books will return all books. How do i make the second endpoint that will return just featured books? is there another way to query, like /books?featured=true ?
thanks.

You have all sorts of options here, but perhaps the best would be to define a route like this:
get '/books(/:featured)' => 'controller#action'
Then, in your controller, something like:
#books = params[:featured].present? ? Book.featured : Book.all
That's just quick-and-dirty, but certainly does the trick.

untested but should work.
routes.rb
get "/books(/:type)" => "books#index", as: :books
books_controller.rb
// kinda of flexi situation, you just throw in a "filter" you want to look for. you throw in "featured" and it will look for "featured=>true". if you throw in "published" it looks for "published=>true"
def index
if params[:type].present?
#books = Book.where(params[:type].to_sym => true)
else
#books = Book.all
end
end
index.html.erb
<% =link_to("all books", books_path) %>
<% =link_to("feature_books", books_path(:featured)) %>
<% =link_to("other scoped books", books_path(:other_scope)) %>

Related

Rails: Using scopes to show different subsets of a model and how to build this the less DRY-ist way?

I have a model movies in my Ruby on Rails application and I want to have a few easy links that show different subsets of it - for example, movies not seen, movies that do not have a production year entered, movies rated as "great" etc.
I have created scopes for all of these conditions in my movie model, e. g.
scope :no_year, -> { where(release_year: [0, nil, ""]) }
But then I want to have a list of these subsets - as said, a list of links where user can click and will get the results in a default view (the movie index view actually). So I have a list of links such as the below, which required me to add routes as well as methods for all of these scopes that look pretty much the same.
<%= link_to 'Movies without a Year', noyear_movies_path %>
(<%= Movie.no_year.count %>)
routes.rb:
resources :movies do
get :noyear, on: :collection
end
movies_controller.rb:
def noyear
#q = Movie.no_year.ransack(params[:q]) # using Ransack for a sidebar that is displayed
#pagy, #movies = pagy(#q.result(distinct: true)) # using pagy to split up results
render 'index'
end
EDIT: added index and sidebar method code.
My index method looks like this:
def index
#pagy, #movies = pagy(#q.result(distinct: true))
end
... and the variable q itself is set application wide, as it is defined in a sidebar that contains a little search field that is always displayed:
def sidebar_q_movie
#q = Movie.ransack(params[:q])
end
I am sure this can be achieved way nicer. But since I am still pretty new to Ruby, I don't know that. Any hints appreciated!
Rather than having separate routes for each movie filter, you could specify the filter with a query param to Movies#index
<%= link_to 'Movies without a Year', movies_path(filter: 'no_year') %>
and in the controller:
def index
#movies = Movies.send(params[:filter])
end
Of course, you would want to validate the filter param first before sending to Movies.

Using stock quote from stock_quote gem as tag from acts_as_taggable gem?

So I am using the tags provided by the acts_as_taggable gem. The posts are what I am tagging. How could I say something (pseduocode here) like =>
if a collection of Posts has a tag with a corresponding StockQuote, display the stock quote
So right now I have a Post resource that acts_as_taggable. Here is what my posts index action looks like now:
def index
#stock = StockQuote::Stock.quote("symbol")
if params[:tag]
#posts = Post.tagged_with(params[:tag])
else
#posts = Post.order('cached_votes_score desc')
end
end
So I would somehow have to iterate through all the #posts tagged_with params[:tag] and compare the tags to stock quotes. And display the quote on the index of all the #posts tagged_with params[:tag] if there is a match. I am going to try to figure out how to limit each post to have 1 tag soon
How could I use the :tag of a post to access a stock quote if that tag happens to be a stock quote?
I think you would want to do something like:
#symbols = #post.tag_list
#quotes = StockQuote::Stock.quote(#symbols)
Since a post could actually be tagged with multiple symbols you will want to fetch them all at once and then iterate over the result in the view. Something like:
<% #quotes.each do |quote| %>
<%= "#{quote.symbol}: #{quote.ask}" %>
<% end %>
I am not familiar with the gems all that much but with quick glance it seems something like this would work.
Also, you phrase the question as if you have a singular instance of a Post but then show your index action which would pull a collection of Posts. The example I provided would work for a singular Post (a user viewing the post's page).

Rails - Execute actions on post's index page

I have a model "Products" (which is a child model of parent model "Assortments") which can be liked by the user. Right now, you can only like the Product on the Products Show page because i'm able to find the products ID, which is what i need to like it
def like
#assortment = Assortment.find(params[:assortment_id])
begin
current_user.vote_for(#product = Product.find(params[:id]))
respond_with #product, :location => assortment_product_path
rescue ActiveRecord::RecordInvalid
redirect_to #product
end
end
I would also like user's to be able to like the product on the products index page. For example, if you go to Pinterest, you can like all the products on their fluid grid layout page as well as on each pictures respective page.
This is one of those things I have yet to learn how to do with Rails. How can i accomplish this??
Thanks in advance! Happy Holidays
Assuming your like action is in your ProductsController, and you have set #products equal to the products you want for your index page, and you're using erb, put something like this in your view:
<% #products.each do |product| %>
<%= link_to "Like", product_link_path(id: product.id) %>
<% end %>
Even if you have it set up differently, it can't be too far off from this.

Return list based on has_many relationship

A State can have many Units. A Unit can only have one State.
On the home page I want to return a list of ONLY the states that actually have units. Right now they are all listed by their name.
So if Arizona and Colorado are the only ones with units...they will only show up on the home page.
I'm new to Rails and I've searched and searched. But, I'm missing something. Seems like it would be some sort of query or filter on the controller? So it is only returning the States that have Units? Any help would be appreciated.
Static page controller
def home
#units = Unit.all
#states = State.order('long_name')
end
Home view
<% #states.each do |state| %>
<%= link_to state.long_name, state %>
<% end %>
JOIN should be enough:
#states = State.joins(:units).order('states.long_name ASC')
or, you can move this to model scopes:
scope :with_units, joins(:units).order('states.long_name ASC')
and call it in controller with:
#states = State.with_units
Controller is not a place for that. It is a typical model concern. This should get you working:
States.all.select {|state| !state.units.empty?}
However it would be much nicer to create the following method for State class:
class State <ActiveRecord::Base
...
def class.with_units
select {|state| !state.units.empty?}
end
end
This will give you:
State.with_units #=> All states with units
But can also be used on association:
user.favourite_states.with_units #=> All user's favourite states filtered to ones with units.

Suggestions needed on Rails route approach

Should my URL's in Rails look like:
http://foobar.com/articles?category=recent
- OR -
http://foobar.com/articles/recent
I find the former to be more RESTFUL, but the latter to be more cleaner (code-wise). For example, the code can look like:
In Article controller:
def index
if params[:category] == 'recent'
#articles = Article.by_recent
elsif params[:category] == 'expired'
#articles = Article.by_expired
end
end
In Article, index view:
<% unless #articles.blank? %>
<ul>
<% #articles.each do |article| %>
<li><%= article.title %></li>
<li><%= article.content %></li>
<% end %>
</ul>
<% end %>
With http://foobar.com/articles/recent, I would have to customize my routes. Something like:
match 'articles/:category' => 'articles#index'
Above will access the Article controller, index action. Or even:
resources :articles do
collection do
get 'recent'
end
end
Which will allow for urls like http://foobar.com/articles/recent, but needs a 'recent' action in the Article controller.
Both seem pretty useful, in the end of the day. But which is the general consensus? Using the query string approach (http://foobar.com/articles?category=recent) or the other way (http://foobar.com/articles/recent).
Looking forward to your suggestions.
Obviously like you said you could go either way, but since something like "recent" is a condition of or query against articles I would go with the former. If it were a nested attribute and its own model the second approach is arguably better, but since it isnt you really lose a lot of flexibility to change and refactor the approach down the road.
For instance if you wanted to do "old" as opposed to "recent" you would not be able to simply pass in an "old" parameter, but would have to hardcode that with its own action and views in the same controller as "recent" which is of course a lot more work for nothing.
In my opinion articles/recent is much cleaner as it avoids filling your controller action with conditionals and also looks nicer from a user perspective.

Resources