Ransack: Can page size be customized? - ruby-on-rails

I'm using Spree and in the products/search controller action it uses Ransack. The Spree documentation says that Spree provides no way to customize page size, so I thought I'd customize the action itself to use Ransack to turn off pagination entirely. Does Ransack support a way to either not paginate results or to at least make the pagination huge?
This is the code in the existing controller action:
#products = product_scope.ransack(params[:q]).result.page(params[:page])
When I puts the type of #products after that, it is ActiveRecord::Relation. When I puts of
#products = product_scope.ransack(params[:q]).result
it is also ActiveRecord::Relation but in the first case there's a method 'total_count' used in the rabl template that is not present in the second one. I have dug through the source for ransack and I can't find where the .page is defined. I'm wondering if perhaps it's included in some class_eval of ActiveRecord::Relation in one of the gems that Spree pulls in.
Someone somewhere has to have faced this problem and come up with a solution. I'm hoping against hope to find that person :P
jd

The .page method comes from the Kaminari gem, and not Ransack.
You can make the pagination "huge" by changing the default per_page value in Kaminari:
Kaminari.config.default_per_page = 1000

Ryan's already got you, but I'm not sure if there isn't another way to do it as well. I can't tell from your question if you're talking about just the number of products shown per page, but it sounds like you are.
If so, I've done the same modification by using the built-in Spree preference products_per_page. When Spree provides a preference, I prefer (no pun intended) to use that over modifying gem parameters since it's part of their design already.
I fired up the console with bundle exec rails c, then ran the following command to set the preference on my store to 16 products per page: Spree::Config.products_per_page = 16
You can also put it in one of your initialization files so it doesn't get accidentally erased if you rebuild your database from scratch...I learned about it from this post in the Spree group. I also found details on setting Spree preferences here.

You want to use .per, for example:
collection.per(100)
collection.page(3).per(100)
collection.per(100).per(3)
Any of these will work.

Related

Rails - ActiveModel::Serializer virtual attribute error

I'm using the active_model_serializers gem for a RoR API. Versions:
Rails: 4.2.8
Ruby: 2.2.5
active_model_serializers: 0.10.0
I'm using a virtual attribute in a serializer. I get it by using a sub query when I retrieve the objects from the database.
You can find the code here: Github Gist
This is the error I'm getting:
undefined method 'number_of_reservations' for DiscountSchedule...
This field isn't defined in the table nor in the model (attr_accessor)
I'm not sure why it doesn't work, I have a very similar serializer and it's working OK.
Any help will be appreciated.
EDIT:
I have another serializer where the virtual/calculated field is working OK. My guess on this is that since AR is making a bunch of LEFT OUTER JOINS and the SELECT list of the query is very big at some point something is getting broke.
The link won't work for me as I don't have access at my work place, however, from the error I can recommend you to check if you have defined the attributes like this in your serializer attributes :number_of_reservations and have an action in the serializer that says
def number_of_reservations
// Your logic goes here.
end
I suspect this question has to be about ActiveRecord, rather than AMS. You're trying to use select and alias to collect some computed (aggregate) attribute along with objects themselves. This, unfortunately, won't work in ActiveRecord, at least not in versions below 4.2.X. And this is why you're observing this behavior, there is no number_of_reservations in your models.
To see what's going on, try to inspect #objects here: https://gist.github.com/LuisDeHaro/ebf92781b449aa1ee7b85f8f552dd672#file-some_controller-rb-L17
Indeed: the issue was by the big amount of LEFT JOINS that the includes(:table_name) is generating. The serializer then does not know what to do.
I found a monkey-patch gem that works for AR (Rails 4 & 5) that fix this.
https://github.com/alekseyl/rails_select_on_includes
So, the virtual field number_of_reservations is picked up by the serializer like a charm.
And, you might be wondering: why do you want to retrieve a field that is not in the table definition in the database. A: well, in some scenarios you will need a calculated field for EVERY row you are retrieving. A SQL sub query is one of the most efficient ways to do so.
It's working now for me.

Using fuzzily with more than one field RAILS

Rails 4.2
Ruby 2.1
Fuzzily (0.3.3)
I am using the fuzzily gem for searches and it's working. In my controller, I would have something like this:
#notes = Note.find_by_fuzzy_description("%#{item}%")
the notes table has the following fields:
user_id
description
contents
What I would like to do is return only the fields where the user_id = 5 (for example). Any idea how to do this with Fuzzily?
As stated in github open issues it's well-known requested feature that is not resolved. You can try to test Note.where(user_id: user_id).find_by_fuzzy_description("%#{item}%") and look if it works correctly, but I doubt it does.
As workaround you can try fuzzy search with big limit and then select results that belongs for user and apply standart limit. But it will be slower, so i wouldn't recommend to use it if you have many users.

Versioning for Rails i18n translations

I'm in the process of building a volunteer based translation engine for a new site built in Rails 4.0. Since it's volunteer based, there is always the possibility that a user may enter a translation that others do not agree with, accidentally remove a translation, etc. In such an event, I would like to give users the option to revert to a previous translation.
I did some searching around but have yet to find a solution aside from writing my own I18n backend. Is there a simpler way of storing previous versions of translations?
I'm currently using Sven Fuchs' Active Record as a backend, however I'm seriously thinking about switching due to possible performance issues later on down the road.
We had a very successful experience using Globalize (github page: https://github.com/globalize/globalize) and as for the versioning part we didn't try it but Globalize does have support for that in a seperate gem github page: (https://github.com/globalize/globalize-versioning)
After tens of painful gem experiences, i found that comparing gems by last update date and how frequent is new releases, bugs fixes and support is a major factor to decide which one will make your life easier and which one won't.
Update:
You can use Globalize to dynamically translate views (check tutorial) but i came across a github project called iye. I think it suits your needs best (github page: https://github.com/firmafon/iye)
I used Nimir's help to find this solution. Like Globalize Versioning, you can add Paper Trail support for the Active Record's Translation class, however there are a few pitfalls to this method currently.
First you need to include the gems in your Gemfile:
gem "i18n-active_record"
gem "paper_trail"
Then you need to make sure your Translation model class is inheriting from I18n Active Record::Translation and call and calls has_paper_trail:
class Translation < I18n::Backend::ActiveRecord::Translation
has_paper_trail
end
This should really be enough, however the store_translations method in I18n Active Record do not update existing records. Instead, each time a record is added, all records with the given key are deleted and a new record is created. This causes confusion for Paper Trail since it relied on an id.
To get around this issue, I created my own store_translation method, which will update records if they exist:
def store_translations(locale, data, options = {})
escape = options.fetch(:escape, true)
I18n.backend.flatten_translations(locale, data, escape, false).each do |key, value|
t = Translation.find_or_create_by!(locale: locale.to_s, key: key.to_s)
t.value = value
t.save
end
I18n.backend.reload!
end
Notice I also included I18n.backend.reload!, this is because I am running Memoize to cache translations but it seems that it needs to be told to recache whenever a record is updated.
Now I can simply call:
store_translations(lang, {key => key}, :escape => false)
to a store a new translation and ensure we keep a record of the old one and who made the change.

Rails: eager load on fetching associations, not on find

I've got three nested models: user has many plates and plate has many fruits. I also have a current_user helper method that runs in the before filter to provide authentication. So when I get to my controller, I already have my user object. How can I load all the user's plates and fruits at once?
In other words, I'd like to do something like:
#plates = current_user.plates(include: :fruits)
How can I achieve this?
I'm using Rails 3.1.3.
You will probably want to use the provided #includes method on your relation. DO NOT USE #all unless you intend to immediately work through the records, it will immediately defeat many forms of caching.
Perhaps something like: #plates = current_user.plates.includes(:fruits)
Unfortunately, there are portions of the Rails API that are not as well documented as they should be. I would recommend checking out the following resources if you have any further questions about the Rails query interface:
Query Interface Guide
ActiveRecord::Relation Walkthrough (screencast)
The query interface is possibly the most difficult part of the Rails stack to keep up with, especially with the changes made with Rails 3.0 and 3.1.
You can do
ActiveRecord::Associations::Preloader.new([current_user], :plates => :fruit).run
To eager load associations after current_user was loased. The second argument can be anything you would normally pass to includes: a symbol, an array of symbols, a hash etc
#plates = current_user.plates.all(:include => :fruits)
should do it.

Thinking Sphinx - Already have a search method in the model?

I'd like to use Thinking Sphinx, but I keep having problems because I have a very large rails project and the search method is used in many of my models. These already existing search methods conflict with Thinking Sphinx's search method. Is there any way around this?
I'm talking thousands of lines of code I would have to change if I had to change my search method to something else. I can't seem to find a way to change the default search method in Thinking Sphinx though either.
Thanks.
Just answered this on the TS list, but happy to answer here as well :)
There isn't any inbuilt way to do this, but in theory it could be possible. Firstly - Thinking Sphinx adds the class-level search method when you call define_index on a model - so, if you define your own search method after that, it'll overwrite the Thinking Sphinx version.
This means you could just define a new method that does the same thing - here's the code for Model.search:
def self.search(*args)
ThinkingSphinx::Search.new *search_options(args)
end
Which you could easily rename to something else:
def self.sphinx_search(*args)
ThinkingSphinx::Search.new *search_options(args)
end
The one possible catch with this is that Thinking Sphinx may have expectations internally on the search method existing and behaving as normal. I'm not sure - but give this a spin and see how you go!
Update:
As it turns out, the above suggestion doesn't cover all situations and it's still buggy. So, I think the fallback solution is to fork Thinking Sphinx, change the method names, and use your version instead of the canonical one.

Resources