Rails 3 Best Practice For Global Data - ruby-on-rails-3

I'm currently rendering a JQuery Plugin on one of my partials in my Application layout. This partial calls one my application helper method which retrieves data generated using Sunspot-solr. The data it's retrieving is the top 30 recently created records created across various models, Uploads, Users, Articles etc. I was wondering what the best practice would be for loading this data if it appears on every view in my application?
Obviously performance is my biggest concern.
This is how I'm currently retrieving the data in my Application helper:
def get_updates
#updates = Sunspot.search(Upload,Help,User) do
order_by(:created_at, :desc)
paginate page: 1, per_page: 30
end
#updates = #updates.results
end

You could fetch that data in a before_filter in your ApplicationController. Then it would be available to any view rendered in any controller throughout the application.
pseudo code:
class ApplicationController
before_filter :load_top_30
private
def load_top_30
#top_30 = Records.find() # whatever code you need to fetch the records
end
end
Now you can use #top_30 in any view. The helper does not (and arguably should not) need to fetch the data since its already in an instance variable now, just pass it in.

If this is only this one item you wish to display globally then doing it in the ApplicationController should be fine, if you have many such elements then you might want to have a look at Cells which basically are reusable sub-controllers which you can call form your views or controllers.

Related

How do I retrieve a random GET request in Ruby on Rails 5?

I've created my REST API based on Michael Scott from the Office, so far if you go onto the main part of the API it displays all the quotes /api/v1/quotes/ and you can get individual ones if you add an id number such as /api/v1/quotes/5,but I want users to be able to get a random quote when they put in a GET request.
This would preferably be done with an extra part of the URL such as /api/v1/quotes/random.
I've looked at a lot online but I can't figure this out. Do I put my code into the quotes controller and create another function or do I have to put this somewhere else such as routes.db? Also do I put in the SQL function of RANDOM or is there a better and more efficient way to randomise it. My database only has 50 records in it and it's done in mysql2.
Thanks in advance.
It's my first time posting here as usually I hate asking for help as I think I can always figure it out myself but I'm extremely new to Ruby so I'm fairly clueless on how to solve this. If you need me to clarify my question then just let me know.
You can do that in Model as well as controller.
Model:
class Model < ApplicationRecord
def self.random
Model.limit(1).order("RANDOM()").first
end
end
Controller: In method show
def show
render json: Model.find_by(id: params[:id]) || Model.random
end
I hope that helpful.
I would configure a quotes/random collection route:
# in config/routes.rb
resources :quote do
get 'random', on: :collection
end
and use a controller method like this:
# in controllers/quotes_controller.rb
def random
#quote = Quote.order('RAND()').first
render :show
end
Please note: Returning a single random row from a table is database specific – for MySQL
ORDER BY RAND() LIMIT 1
seems to be the way to go.

get all records via operation and show in view

I have trouble with trailblazer when setting up a simple show all Things view.
operation
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
include Model
model Thing, :all #why :all is not working here?
def process
end
end
end
controller
class PageController < ApplicationController
def index
run Word::ShowAll
end
end
why is :all not working for getting all Things from the db but :find works to get one via its id?
The best place to ask TRB questions is actually on Github channel.
I'm not sure where you found that example, as it is not supposed to work AFAIK, :find is a shortcut I believe, I've never actually used it.
All your logic should be defined inside the process method. http://trailblazer.to/gems/operation/1.1/api.html#process
Having said that, trying to get all records without some kind of pagination is a really bad idea, unless you are 100% sure that your table won't grow beyond several dozen records. Unless you know you don't have a large load. So to define that kind of shortcut is dangerous.
Calling Trailblazer::Model#model as you're doing there is just a shortcut for overriding the TrailBlazer::Operaration#model! method. So what you seem to want to do is:
class Thing < ApplicationRecord
class ShowAll < Trailblazer::Operation
def model!(params)
Thing.all # add any filtering or pagination here
end
end
end
And in your controller call present instead of run so that it sets up the model but doesn't call the process method of the operation.
class PageController < ApplicationController
def index
present Word::ShowAll
end
end

Baffled by results of the render command in Rails

Lets say you have a post with comments on the same page, and you render a form for capturing a new comment also on the same page as you are displaying the post/comments. A post has_many comments. Code as follows:
class PostsController < ApplicationController
...
def show
#post = Post.find(:params[id])
#comment = Post.comments.new
end
...
end
Now when you call <%= #post.comments.count %> in your views it gives the number of comments that have been saved, but if you call <%= render #post.comments %> it returns all the saved comments PLUS the newly created (but not yet saved and therefore still invalid) comment. Why is this? This has really taken me time to find this and I can't imagine that this would be useful, why not just render all the valid database records?
Has anyone else ran into this? Easy to fix but puzzling..
Well, #post.comments.count actually does a database query and can therefore only return the number of saved records. (Use #post.comments.size or .length) for the number of objects in your collection.
The render call, AFAIK, only loops over the objects in the collection.
The thing to know here is the difference between when you do actual queries with the association, and when active record is using the cached objects. It is perhaps easy to assume that the comments in #post.comments is just an Array. It actually is a fancy proxy object that, depending on method called and state of the cached collection, acts like an Array or as an interface to the Model's query methods.

What is the best way to translate slugs in Rails routes

I'm trying to achieve full internationalization of my routes in a Rails3.1 app. I'm already using Francesc Pla's rails-translate-routes to localize route actions and resources. The last step is to be able to translate slugs for some of my models.
Route to be translated:
http://myhost.com/products/pharmacy --> http://myhost.com/productos/farmacia
I have a nested route of the form
# routes.rb
match 'products/:category_slug' => "products#index"
I have a model Category with an instance #<Category id: 1, slug: "pharmacy"> and I do find_by_slug category in my ProductsController.
Any ideas on how to do translate the :category_slug part of the route?
As far as I'm aware, you can call translation helpers directly from your controller as long as you namespace correctly with I18n.
So your ProductsController could contain something like the following:
class ProductsController < ApplicationController
def index
i18n_slug = I18n.t("locale.category.#{params[:category_slug]}")
#category = Category.find_by_slug(i18n_slug)
end
end
You should probably inform yourself as to potential security risks of passing the params directly into the translation engine, though I'm not aware of any. You might also consider
moving that into a before filter or into the application controller if it will be used in multiple controller actions.

Create a 'Random User' button on my Rails 3 web app

I want to have a button which goes to a random user on my site. I am using the friendly_id gem so the URLs are, for example, /users/dean and I've also set it up so its /dean.
I'm guessing I would add something similar to this in my routes.rb file:
match '/users/random' => 'users#index'
And then some extra code in the user controller?
How would I go about doing this?
Many thanks.
I'd do this:
Define a class method random on User model (or in a module that's included into your model if you'd want to reuse it for other models later).
class User
def self.random
offset = rand(count)
first(:offset => offset)
end
end
Other ways of getting a random record, if performance becomes an issue.
Add a random action in your UsersController like this
def random
redirect_to User.random
end
And finally create a route
match '/users/random' => 'users#random'
I would have a specific action random in the user controller and localize the logic for choosing a user there. Return a redirect to the route to that user from that action. I would prefer this over complicating the index action with extra logic to handle a different action.