Parsing multiple IDs from a URL in Rails - ruby-on-rails

I'm building a sort-of Twitter clone, with (amongst others), models and Hashtag and Post, which have a has_many: through: relationship with each other.
Currently, visiting /hashtags/pizza will take you to a page that shows all posts with hashtag the hashtag #pizza. I want to add functionality so you can pass multiple hashtags in a URL and see the posts which contain all of those hashtags. For example, if you visited /hashtags/pizza/pasta (or hashtags/?pizza&pasta' or whatever's easiest to implement) you'd be shown a page containing all Posts which have BOTH the hashtag #pizza and the hashtag #pasta.
How would I do this? I have no idea where to begin.
For the record, my imagined code in the controller would be something like this. Feel free to poke holes in it:
1 def show
2 requested_hashtags = ??????? # an array of hashtags e.g. ["music", "guitar"]
3 hashtag1 = Hashtag.find_by_name(requested_hashtags[0]) # in this case r_h[0] = music
4 hashtag2 = Hashtag.find_by_name(requested_hashtags[1]) # and r_h[1] = guitar
5 #posts = hashtag1.posts & hashtag2.posts
6 end
Then in my view I'd just iterate over #posts. (The above example is dumbed down in that it assumes there's always exactly two hashtags requested but the principle is the same.)
So my question is, how do I complete line 2 of the code snippet above?

You can treat the url like any other string, so you could create a url like:
hashtags/?tags=pizza,pasta
And then in your controller (line 2):
requested_hashtags = params[:tags].split(",")
Another more commonly used approach in Rails is:
hashtags/?tags[]=pizza&tags[]=pasta
and then in your controller:
requested_hashtags = params[:tags]
This second way is a pretty standard Rails convention.

Related

How do I generate all new records for a given model randomly using pre-defined options?

I'd like one of my models, Stones, to be generated at random using pre-defined options I've stored in a set of arrays and hashes. Instead of Create using params from the URL, I'd like new Stones to always be defined using this random generation process. I don't need any user input at all, except that each stone belongs to a given player.
I'm still new to rails; where should I put all this code? I know enough to be able to define the arrays and hashes and randomly select from them when I need to, but I'm not sure where and how to replace the part of the code that draws params from URLs and fills in a new record before it is saved. I know controllers are supposed to be skinny, so do I do this in the model?
Apologies if this is a duplicate. I searched extensively and couldn't find an applicable solution.
Thanks for any help!
I would create a service for this. Something like:
# app/services/stone_creator.rb
class RandomStoneCreator
RANDOM_FOOS = ['bar', 'baz', 'bat']
def self.call(user)
Stone.create!({
foo: RANDOM_FOOS.sample,
user: user
})
end
end
And then anywhere that you need a new random stone you can call it like:
random_stone = RandomStoneCreator.call(current_user)

How to get a display in descending order

I know there is a simple answer to this question but I have searched for 2 days trying to find it. I have a program in rails that lists workers in the home building industry. I ask for the following informatio occupation, first name, second name,telephone number, email address. This information is displayed without any editing of my code. I would like to display this information using the order query and showing the information using the occupation column in descending order. Please tell me the code to use and where it goes.
I assume you have a method named index inside your controller. Ok, inside the index method, you can use .order method.
Make it like this:
def index
#users = User.order(occupation: :desc)
end
Reference:
http://apidock.com/rails/ActiveRecord/QueryMethods/order

Design - How to structure filters taking advantage of OOP in Rails API app

My app shows items in a feed. This items are ratings made by different users. Ratings can have a value, a review, etc.
Usual case is logged in user asking for feed (/users/feed). The controller takes the action:
def feed
if authenticate_with_token
imos_and_cursor = Feed.new(RandomItemFeeder.new(params[:cursor], #current_user)).feed
render json: {cursor: imos_and_cursor[imos_and_cursor.length-1], imos: imos_and_cursor[0..imos_and_cursor.length-2]}
end
end
Feed is the boss here. It controles what is served (it serves to respond with items but it also will know how to respond for feeding the people call (index of users basically).
Here are some of the feeders I have:
FriendsFeeder
RandomItemsFeeder
MostRecentItemsFeeder
Following is RandomItemFeeder, responsible of feed with random items:
class RandomItemFeeder < Feeder
def feed
influencers_ids = User.influencers.distinct(:id)
friends = User.friends(#watching_user).distinct(:id) if #watching_user
source_users = influencers_ids.concat(friends)
Rating.random_from_influencers(#watching_user, source_users).scroll(#current_cursor) do |rating, cursor|
#next_cursor = cursor
#feed << ImoPresenter.new(Imo.new(rating), #watching_user)
end
#feed << #next_cursor.to_s
end
end
Presenters
I've structured the rendering of jsons with presenters, so I have different presenters for different cases (feed, user profile, etc.).
Now, I want to incorporate filters. For example, I want that RandomItemFeeder feeds with just items of last 5 five years (obviously, Item model has corresponding fields).
The question is how can I incorporate this filtering utilizing best OOP practices. At the end, is just a scope and i can implement it in different ways, but I just want to do it right now so that I don't have to come back and refactor everything.
Thanks in advance.

Simple recursive method

I have Board model. Board can be subscribed to other boards (as a feed).
Lets say I have board tree like this:
http://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Binary_tree.svg/200px-Binary_tree.svg.png
So:
Board.find(2).feeds are boards 5 and 7
Board.find(7).feeds are boards 2 and 6 etc.
I want to write method all_feeds which returns all feeds from all levels for certain board. For example:
Board.find(7).all_feeds would output array of boards with ids: 2,6,5,11
I started with something like:
def all_feeds
if feeds.empty?
return
else
feeds.each {|feed| feed.all_feeds}
return feeds
end
end
Probably have to add this return feeds to some global array, but not sure how should I do this.
Thanks for help.
ps. this is not always a binary tree, you can have more than 2 feeds.
I guess that what you want could be achieved with:
def all_feeds
unless feeds.empty?
feeds + feeds.map(&:all_feeds).flatten.compact
end
end
Array#flatten makes the result one-dimensional, while Array#compact removes the nil components.
For an explanation of the map(&:all_feeds) part, you can refer to this SO answer :)
Looks like it's working for below code:
def all_feeds
if feeds.empty?
self
else
[self]+feeds.map(&:all_feeds)
end
end
if it is allowed to use gems ancestry gem will help do the trick
Board.find(7).descendants
in this case it will be definitely one request to db without any recursion which is better for performance
you can implement ancestry idea without gem (or in top of it):
add ancestry field to your model
fill it correctly when you build your tree (for nested nodes with ids 2 and 6 it will be 2/7, with ids 5 and 11 - 2/7/6 )
and then just take it from db with like 2/% query

break down a complex search query in Rails 3

I have a controller which has a lot of options being sent to it via a form and I'm wondering how best to separate them out as they are not all being used simultaneously. Ie sometimes no, tags, sometimes no price specified. For prices I have a default price set so I can work around with it always being there, but the tags either need to be there, or not. etc.
#locations = Location.find(params[:id])
#location = #locations.places.active.where("cache_price BETWEEN ? AND ?",price_low,price_high).tagged_with([params[:tags]).order(params[:sort]).paginate :page => params[:page]
I haven't seen any good examples of this, but I'm sure it must happen often... any suggestions? Also, even will_paginate which gets tacked on last should be optional as the results either go to a list or to a google map, and the map needs no pagination.
the first thing to do when refactoring a complex search action is to use an anonymous scope.
Ie :
fruits = Fruit.scoped
fruits = fruits.where(:colour => 'red') if options[:red_only]
fruits = fruits.where(:size => 'big') if options[:big_only]
fruits = fruits.limit(10) if options[:only_first]
...
If the action controller still remains too big, you may use a class to handle the search. Moreover, by using a class with Rails 3 and ActiveModel you'll also be able to use validations if you want...
Take a look at one of my plugins : http://github.com/novagile/basic_active_model that allows you to easily create classes that may be used in forms.
Also take a look at http://github.com/novagile/scoped-search another plugin more specialized in creating search objects by using the scopes of a model.

Resources