AngularJS scoped rails query - ruby-on-rails

I just started learning AngularJS integrated with a Rails backend. I'm confused about where scoped query logic belongs.
I have simple blog with a list of posts and two links which should sort the posts by "newest" and "most voted".
With angular, where is this scope logic? I see there is a 'filter' option on ng-repeat, but it seems inefficient to query Post.all in my index action if there are thousands of posts and then use filter to show the top 10 most voted?
I'd appreciate a simple explanation of what should take place from the point of a user clicking the "most voted" link" to interacting with the back-end API to properly scope the query.

I would add a param order_by in the request to PostsController. Using this param you can decide how to order posts fetched from the database and the send then back to angular.
It's worth noting two things:
Don't use Post.all because it's inefficient to fetch all the records to the application and even worse to send all of them to the client. Use pagination: fetch a certain number of posts. You can add pagination easily using gems like kaminari or will_paginate
Remember not to put params[:order_by] value directly as a Post.order() argument, because it might cause SQL injection. It's mentioned in this Railscast

Related

Ruby on Rails: Implementing search. How should I format my route for many parameters?

I'm building my first Ruby on Rails application, and I want to do it the "Rails" way, making my application as RESTful as possible.
The application revolves around a search function, in which a user fills out a form defining what they are looking for, submits it, then pages through their results, viewing items they are interested in. From my understanding of REST, it sounds like I should be encoding my parameters into the URL for my results page(?)
If my base route is:
match "search" => "search#index"
And the base route for my search results are:
match "search/results" => "search#results"
What would be the best way of implementing routes/URLs/parameters for my search? The solution must:
Support many parameters (30+), of which, any could be optional.
Support pagination: there could be alot of results.
Adhere to the "Rails way"!
Any suggestions/tips from you Rails pros would be very helpful, thanks!
For pagination: you can use a gem like will_paginate or kaminari
Concerning search parameters: Rails will put anything in the request's query string into a params variable accessible within your controllers and views.
What you're trying to do (naming the urls search & search/results) doesn't really align with "restful routes". Still, this is what you've asked for.
scope :search do
get 'search' => 'search#index'
get 'search/results' => 'search#show'
end
will produce
search GET /search(.:format) search#index
search_results GET /search/results(.:format) search#show
To remain restful, try discerning what you're searching for... for products, the results would be displayed using Products#index.
If you want to keep the search broad, I'd suggest Search#new and Results#index. These latter options seem more RESTful.
As you are probably anticipated, passing those 30 params around as url parameters is probably not the best approach.
I would consider creating a Configuration model (and table) for them and focus on the form being able to update this model. This will make storing the searches, doing multiple searches, validating the searches much easier in IMHO. Use this model in conjunction with the actual model, to do the searches.
For the pagination, as rthbound says use will_paginate or kaminari.

Search action in ReSTful rails model

So, I'm quite new to Rails and still working my way through the principles. I suppose like most people, I have started creating that basic CRUD. Okay. Done.
Now I want a new action: search. As it turns out, it is not one of the 7 rest sacred (!) actions (if got it right). While I know I could implement new custom actions and resource it and everything, I read in a few places to try my best to stick to the standard ones as long as possible. Okay. What would be the correct way?
Again a few sources like this guy suggest thinking of my scenarios in therms of nouns, case in which seems I'd need a "search" controller...? It just doesn't convince me that I'd have to create a whole class whereas I'd normally do def search just to keep it ResTful.
What did I get wrong? What would be the common solution here?
thanks.
REST is a concept, not a religion :-). But the core verbs are GET/POST/PUT/DELETE which map to their associated HTTP verbs. What's in the URL is typically a reflection of this, and (this is more the Rails philosophy) following convention can make everything much easier. The URLs you get with generic rails (e.g. scaffold) are not particularly ideal in several ways, but they work, and you can change them.
So, yeah, for search (assuming it's starting simple, e.g. finding records in a single model, say Product) then you could do a GET with a query string like this
def search
#results = Product.where("name ILIKE ?", params[:query])
...
end
Which would result in a URL like /product/search?query="foo" -- nothin' wrong with that.
It depends if your search is against ONE resource or many resources. For example if you have a ProductsController and you want to implement a search feature only for your products, you could create a collection action called "search" (the url would be /products/search)
If your search is for many resources, I'd create a SearchesController with a singleton resource :search in my routes file.
Then again, when you implement search functionality in your application, don't put all the logic in your controller but create models classes to handle your search. You can even create an abstract class to map to your search form and thus avoid using '*_tag' fields to create your search form.
See : https://github.com/slainer68/basic_active_model
If you want to adhere to REST (which is a guideline really, it has pros and cons), then the slideshare you link to is recommending the right way to do things.
So, for example if you have a comments_controller, and you want to be able to search comments, you could create a comments_search_controller. The search form would be at comments_search_controller#new, which would POST to comments_search_controller#create.
Yes, you are creating another class doing it this way, but that's not much different than creating another action in the comments_controller, and it does keep things consistent and separated. You wouldn't need a new CommentSearch model or anything, just that controller, which asks your Comment model for the relevant search results.

custom action for a nested resource without adding to routes

This is probably really simple but I have a nested resource lets say:
map. resources :book, :has_many => :pages
I write an action called "turn" that increases page.count by 1. How do I call this action using a link_to? Thanks a lot in advance.
It's hard to tell where your page.count comes in. In Railish, you would find pages.count (note the 's'). Further, count (and also size) is a read-only attribute on arrays and hashes et.al. provided by ruby that returns the number of elements. You don't set count.
Next, I'm not sure where your turn action is supposed to live, on the Book or the Page? And what is supposed to happen after it does what it does? Finally, a route is what makes an action an action -- without it, it's just a function.
For a moment, we'll assume you are trying to store the number of times a Page in a Book has been visited. It would be a better idea to have an instance variable called reads or times_viewed etc. in your Page model. Assuming your Book model is using restful routing, in Book's show action, you create an instance variable of the Page model being viewed and increment its reads attribute before rendering the view.
If you are trying to make a link sort of how 'Like' works in Facebook, meaning you want to update a record in a table without sending the user to a different page, you'll need to use link_to_remote* and some javascript. In that case, I'd just refer you to the Railscasts on that subject.
*I think as of Rails 3, link_to_remote became just link_to with :remote => true.

Using rails resources (REST), how can I use a form to filter results on GET requests (i.e. /products)

I'm new to rails and having trouble figuring out how to do this RESTfully. I have a route set as map.resources :products and what I want to do is have some way to filter the results returned from the index action. For example, you go to /products which calls the index action per the resources default, then on that page there is a filter form on the left side. You may check certain filter options and then submit this form, which would then just render basically the same page, but with filtered results (i.e. everything that costs less than $X).
Originally I was thinking to just check the HTTP method in the products controller's index method, if get, display as normal, if post, apply filter. However, this is not RESTful and I would like to figure out a RESTful way to do this, which is also elegant (code-wise). Thank you for the help.
I guess I could use a query string, but I would prefer to keep the url clean as once it's working normally I'd like to use some AJAX to update the list on the fly.
This is precisely what query parameters are for. You are still using the same REST action, i.e. an index fo products. For you example, the URL should be
/products?max_cost=x
In your controller, your index action should just look at params[:max_cost] or whatever other filters you want to support, to modify the query and display the results.

Rails best practice for having same form on multiple pages

I am developing an Rails 2.3.1 Web site. Throughout the Web site, I need to have a form for creating Posts on various pages (Home page, Create Posts page, Post listing page, Comment listing page, etc. -- suffice to say this form needs to be on many pages served by a variety of controllers). Each of these pages displays a wide variety of other information that is retrieved in the corresponding controller/action. Ex, the home page lists latest 10 posts, content pulled from the DB, etc.
So, I've moved the Post creation form into its own partial, and included this partial on all the necessary pages. Note that the form in the Partial POSTs to /questions (which routes to PostsController::create -- this is default rails behavior).
The problem I am running into is when the the Posts form is not completed correctly, by default the PostsController::create method render's questions/new.html.erb, even if the form was submitted from the home page (/home/index.html.erb).
I tried changing the form in the partial to submit the "submitting_controller" and "submitting_action", and in PostsController::create, when #post.save? == false, I render action => "../submitting_controller/submitting_action" (Which is slightly hacky, but lets you render actions from non-PostsController's).
This seemed to work OK on the surface. The incomplete form was rendered in the view that submitted it with all the correct #post.errors message, etc. The problem was ALL the other data on the pages didnt show up, because the actual submitting_controller/submitting_action methods weren't called, just the associated view. (Remeber, I did a render which preserves instance objects, rather than a redirect_to which does not preserve the #post instance object which has all the error messages and submitted values.)
As far as I can see I have two options:
1) I can store the #post object in the session when #post.save? fails in PostsController::create, redirect_to submitting_controller/submitting_action, at which point i pull the #post object out of the session and use it to re-populate the form/error messages. (As far as I understand, storing objects in the session is BAD practice in rails)
2) I can move all the logic used to pull non-post creation form data from the various submitting_controller/submitting_action, put it in the ApplicationController, create a giant switch statement in PostsController::create for submitting_controller/submitting_action and call the methods in the ApplicationController to grab all the extra data needed for each submitting page's render.
Thoughts on the best way to do this within Rails?
By any chance is your Post model in a belongs_to relationship with each model who's controller you'll be using to render your form? Are you doing any special processing in the Post controller beyond Post.create(params[:post])?
If you answered yes to the first question and no to the second, you could get by with minimal mangling by adding accepts_nested_attributes_for to each of the controllers on which a user can create a post.
Regardless, Robertpostill is correct in that this is probably when to start looking at AJAX, to just replace the section of the page. The only problem is what to do if a user has javascript disabled. Personally I like to do design for the non-javascript case and add convenience methods.
As for thoughts on what you consider your two options,
1) I've used this method to store a shallow copy of an object in the flash hash. Preserving it across redirects. However this might not work for you given the variable nature of posts. As you can only send about 4K worth of data, and that includes other information in addition to your shallow copy.
2) See robertpostill's response
This is about the point that you move from full page updates to updating sections of a page through the use of AJAX. There are a bunch of things you should consider but the most rails-esque approach would be to split the response between an AJAX response and a plain HTML response. Check out this ONLamp article, this register article or the awesome agile web development with rails book. Essentially your controller renders a new div replacing the old div containing the result of submitting the partial.
In your question you mention two approaches and so I'll try and give you some pointers on why and why not here:
Option 1) Ths option is not so bad with a couple of tweaks. The main tweak is is to store the object in a serialized form in the DB. Then simply pass around the ID of the serialized object. Your upsides are that the session data gets persisted so recovering a a session is neater and your session stays light. The downside of this is that having a bucket of session cruft in your DB will pollute your app and you'l need to do some thinking as to how you expire unused session cruft from the DB. I've never seen this end well...
Option2) Eeek not inside the application_controller! :) Seriously, keep that as your weapon of last resort. You can pop things insde the helpers though and get access to those methods inside your controllers and views. However the testing of that stuff is not so easy so be careful before choosing that route. Switch statements can be replaced in OO apps with a little thinking, certainly in his case you can use option hashes to get a railsy way of having some smarts about the state of the app at the time the request is made.

Resources