Rails, filterrific with multiple parameters - ruby-on-rails-4

I'm using the most recent ruby + rails, with the filterrific gem. It works great - but how can multiple parameters per scope be used? For single column filtering it is simple, but for the following scenario, how would it be handled?
Filter by X miles of zipcode Y
Scopes currently only have knowledge of the value being modified (EITHER miles, OR zipcode - two different scopes), but neither have knowledge of the other. This filter scenario requires knowledge of miles and zipcode. I have searched the documentation thoroughly and see no way. Has anyone done this before or am I missing something hidden in the documentation?

You can use Rails' fields_for form helper to submit a hash with multiple keys and values to a single filterrific enabled scope:
<%= f.fields_for :with_distance do |with_distance_fields| %>
<%= with_distance_fields.text_field :max_distance %>
<%= with_distance_fields.text_field :city %>
<% end %>
Then you can access the various values in the scope like so:
scope :with_distance, lambda { |distance_attrs|
# `distance_attrs` is a hash with two keys:
# {
# :max_distance => '10',
# :city => 'Vancouver',
# }
where(...)
}
This is tested with Filterrific 1.4.2 and Rails 4.1

Related

Rails 4 Fragment Cache Entire Page. How to Expire Cache In Model

How do I expire the main-page fragment in a model?
In my HTML
<% cache 'main-page' do %>
# html here
<% end %>
In my Post Model
after_create :clear_cache
after_update :clear_cache
def clear_cache
ActionController::Base.new.expire_fragment('main-page')
end
This doesn't clear the cache. If I create or update a post, the cache doesn't clear. If I run ActionController::Base.new.expire_fragment('main-page') in rails console it returns 'nil'. If I run Rails.cache.clear instead of ActionController::Base.new.expire_fragment('main-page') in the post model, it works.
I believe your issue is the digest, so if you change your cache to this it should work:
<% cache 'main-page', skip_digest: true do %>
# html here
<% end %>
If you want to use this kind of style, where the cache doesn't expire and relies on detecting model changes to invalidate, you may want to consider using an Observer or Sweeper, which were removed from Rails 4, but are useful for this pattern:
https://github.com/rails/rails-observers
Not the answer you are looking for perhaps, but another way:
Make the cache key based on the max updated_at from the Post model.
Whenever any post changes, the cache key will automatically miss and go retrieve the latest posts to re-cache that part of the view.
module HomeHelper
def cache_key_for_posts
count = Post.count
max_updated_at = Post.maximum(:updated_at).try(:utc).try(:to_s, :number)
"posts/all-#{count}-#{max_updated_at}"
end
end
And then in your view:
<% cache cache_key_for_posts, skip_digest: true do %>
# html here
<% end %>

url_for in an email sent by a rake task in rails 4

I have a rake task that sends out daily digest emails of player activity during a day. (See example code below.) If I run PlayerActivityMailer.activity_report.deliver in my console, everything works just fine. However, when I try to invoke the rake task, I get the following error:
rake aborted!
ActionView::Template::Error: arguments passed to url_for can't be handled.
Please require routes or provide your own implementation
After doing some research, I found that in Rails 4, they totally nerfed ActionView::Helpers::UrlHelper.url_for (http://apidock.com/rails/v4.1.8/ActionView/Helpers/UrlHelper/url_for - notice the giant red minus sign under the 4.0.2). If you look at the source, you can see the error I'm seeing - it no longer takes options. As far as I can tell, that functionality still exists in other url_fors, such as the one in ActionDispatch::Routing::UrlFor. Also, the error message suggests including Rails.application.routes.url_helpers.
What I've tried
include ActionDispatch::Routing::UrlFor in both the rake task (inside the task) and the mailer (both at the same time, and each separately)
include Rails.application.routes.url_helpers in the same places and configurations, both with and without the UrlFor include.
The error still persists. My guess is that the page view is still insisting on using the ActionView::Helpers::UrlHelper version of url_for. I don't think I can include things actually in the views (which is sloppy looking and hacky even if I could).
Example Code
(heavily sanitized)
config/environtments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost:3000' }
lib/tasks/player.rake:
namespace :player do
task :activity => :environment do
PlayerActivityMailer.activity_report.deliver
end
end
app/mailers/player_activity_mailer.rb:
class PlayerActivityMailer < ActionMailer::Base
def activity_report
#activities = PlayerActivity.all
mail(to: 'foo#bar.com', subject: 'activity report')
end
end
app/views/player_activity_mailer/activity_report.html.erb:
<% #activites.each do |activity| %>
Player: <%= link_to activity.player.name, player_url(id: activity.player.id) %>
...
<% end %>
I also have a model Player, resources :players in my routes.rb file, and a PlayerActivity class with an association to Player.
I'm currently using the (really horrifying) workaround of #base_url = Rails.configuration.action_mailer.default_url_options[:host] in my mailer action and "http://#{#base_url}/players/#{activity.player.id}" in my view instead of the player_url part.
Help!
Have you tried passing just your player in the URL? Like this:
<% #activites.each do |activity| %>
Player: <%= link_to activity.player.name, player_url(activity.player) %>
<% end %>

f.input collect: is showing duplicates in the select

I have the following simple_form input:
<%= f.input :user_id, collection: [options_for_select(User.all.map{ |u| [u.firstname, u.id]})] %>
There are 3 users in my local database. When I use the select in the form it shows the users twice like:
Tony
Johnny
Bill
Tony
Johnny
Bill
I'm not a pro with the map syntax above, so it may have to do with that.
I found this as a much better way to code the select. Also, the one I was trying above doesn't work on Heroku. This is what I used to make it work:
true) %>

Rails 4: tell simple_form to use input type "date" instead of showing different select boxes like Rails 4's f.date_field?

When I use Simple Form it shows three different select boxes for column type date. Instead of this I want Simple Form to show date attributes as HTML5 does. Specifically I want Simple Form to do the following:
<input type="date" name="org[established_at]" id="org_established_at">
For this I tried:
<%= f.input :established_at, as: :date %>
But this produces three different select boxes for date picking.
How do I tell Simple Form and Rails 4 to use input type "date"?
Recently I've done it like this:
f.input :established_at, as: :string, input_html: { class: :datepicker }
and used http://xdsoft.net/jqplugins/datetimepicker/ to show the calendar
In my project I used next:
= f.input_field :year, as: :datepicker, class: "string form-control teacher_kpk_year", readonly: true
Gemfile:
gem 'bootstrap-datepicker-rails'
applications.js
//= require bootstrap-datepicker
If you have a field defined as date, you only need add the parameter html5 like this:
f.input :established_at, html5: true
But, if you haven't defined your field like date or something like that, you only need to add the alias to the previous declaration, like this form:
f.input :established_at, as: :date, html5: true
And both forms will render a date or datetime picker like bootstrap-datepicker
best regards,

How to use activemodel to validates date_time_select

I am using rails 4.0
I wonder how to validate date_select with activemodel
let's suppose I have the code as follow
app/models/book.rb
class Book
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :title, :written_on
validates :title, presence: true, allow_blank: false
# How validates :written_on if I use date_select Please look in my view below
end
app/controllers/books.rb
class BooksController < ApplicationController
#...
def new
#book = Book.new
end
def create
#book = Book.new(book_params)
if #book.valid?
#Do something
end
end
#...
end
app/views/books/new.html.erb
<% form_for #book do |f| %>
...
<%= f.date_select :written_on %>
...
<% end %>
I have also try adding
attr_accessor 'written_on(1i)'
to my book model but I got the error invalid attribute name 'written_on(1i)'
Really appreciated for the help here.
Just to clarify, I think you're asking why you're not even able to set the written_on attribute, let alone validate it-- when I used your exact code locally and tried to create a new book, on submit I got undefined method `written_on(1i)=' for Book.
This is because the Book model isn't inheriting from ActiveRecord::Base; you're just including ActiveModel::Model and ActiveModel::Validations. The Rails guide on form helpers says "when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type."
So I started looking through the Rails source to see where this functionality was implemented, and it's currently in ActiveRecord::AttributeAssignment. There is currently an open pull request that moves this functionality to ActiveModel so that in cases like yours, you'd be able to use it by including ActiveModel::AttributeAssignment.
I'm not sure what you can do until that gets merged in and released. I tried including ActiveRecord::AttributeAssignment and still got the same error, and looking at the pull request, it doesn't seem to be that straightforward. You could fork Rails and apply that pull request, but you'd have to maintain your own Rails for a while until that lands, then get back on a released version.