Kaminari is great to create Twitter-like pagination:
=link_to_next_page(#results, t('kaminari.next'), :remote => true, :class => 'kaminari-next', :rel => 'next')
However, anyone has any idea to make it support blocks? I'd like to add a font-awesome icon:
=link_to_next_page #results, :rel => 'next', :remote => true, :class => 'kaminari-next' do
%i.icon-chevron-down.icon-2x
%br
=raw(t 'kaminari.next')
This doesn't seem to work.
I found the definition of the method by looking at the source code of Kaminari.
# A simple "Twitter like" pagination link that creates a link to the next page.
#
# ==== Examples
# Basic usage:
#
# <%= link_to_next_page #items, 'Next Page' %>
#
# Ajax:
#
# <%= link_to_next_page #items, 'Next Page', :remote => true %>
#
# By default, it renders nothing if there are no more results on the next page.
# You can customize this output by passing a block.
#
# <%= link_to_next_page #users, 'Next Page' do %>
# <span>No More Pages</span>
# <% end %>
def link_to_next_page(scope, name, options = {}, &block)
params = options.delete(:params) || {}
param_name = options.delete(:param_name) || Kaminari.config.param_name
link_to_unless scope.last_page?, name, params.merge(param_name => (scope.current_page + 1)), options.reverse_merge(:rel => 'next') do
block.call if block
end
end
This states that when you pass a block, the block content is what is displayed when there are no more results on the next page. So it seems it does not support what you are trying to achieve. Maybe you can add what you want displayed manually and then convert that code into a partial.
Related
I added the simple_captcha gem and I'm receiving an error in the server when I load up the form that it's located on. I'm not sure how to go about fixing this. Judging my the research I've done on google it appears to be an ImageMagick issue but I'm not entirely sure.
SimpleCaptcha::SimpleCaptchaData Load (0.2ms) SELECT "simple_captcha_data".* FROM "simple_captcha_data" WHERE "simple_captcha_data"."key" = '406a6bd66e520eef7f24a13c2cc8c5b23d3749e8' LIMIT 1
CACHE (0.0ms) SELECT "simple_captcha_data".* FROM "simple_captcha_data" WHERE "simple_captcha_data"."key" = '406a6bd66e520eef7f24a13c2cc8c5b23d3749e8' LIMIT 1
StandardError (Error while running convert: convert: unable to read font `/usr/local/share/ghostscript/fonts/n019003l.pfb' # error/annotate.c/RenderFreetype/1125.
convert: Postscript delegate failed `/var/tmp/magick-4563zAoZ5QYyFe6C': No such file or directory # error/ps.c/ReadPSImage/833.
convert: no images defined `/var/folders/6b/tq59gs0d1f7bp_zg41cf6fqm0000gn/T/simple_captcha20130626-4555-1yk8sq1.jpg' # error/convert.c/ConvertImageCommand/3078.
):
simple_capthca.rb
SimpleCaptcha.setup do |sc|
# default: 100x28
sc.image_size = '120x40'
# default: 5
sc.length = 6
# default: simply_blue
# possible values:
# 'embosed_silver',
# 'simply_red',
# 'simply_green',
# 'simply_blue',
# 'distorted_black',
# 'all_black',
# 'charcoal_grey',
# 'almost_invisible'
# 'random'
sc.image_style = 'simply_green'
# default: low
# possible values: 'low', 'medium', 'high', 'random'
sc.distortion = 'medium'
sc.image_magick_path = '/usr/local/bin/' # you can check this from console by running: which convert'
end
reservations_controller.rb
def create
#restaurant = Restaurant.find(params[:restaurant_id])
#reservation = #restaurant.reservations.build(date: DateTime.new(params[:date]["date(1i)"].to_i, params[:date]["date(2i)"].to_i, params[:date]["date(3i)"].to_i), time: (params[:time][:hour]).to_i)
if #reservation.valid_with_captcha?
#reservation.save
#restaurant.owner.send_reservation_notification(#reservation)
redirect_to restaurant_path(Restaurant.find(params[:restaurant_id]))
else
flash[:error] = "Captcha incorrect. You enterd the wrong digits."
redirect_to :back
end
end
new.html.erb
<h1>Reserve your table.</h1>
<br/>
<div class="reservation-form">
<%= form_for([#restaurant, #reservation]) do |f| %>
<%= date_select :date, "date" %><br/>
<%= select_hour Time.now, :ampm => true, :prefix => :time %><br/><br/>
<%= f.simple_captcha :label => "Prove that you're not a robot." %><br/>
<%= f.submit "Make reservation", class: "btn btn-primary" %>
<% end %>
</div>
Reservation.rb
class Reservation < ActiveRecord::Base
attr_accessible :restaurant_id, :date, :time
belongs_to :restaurant
apply_simple_captcha
end
Probably, you need to install ghostscript after you have imagemagic installed.
I have the following code that generates a list of superlatives:
<%= render :partial => 'superlative', :collection => #profile.superlatives %>
The :partial code referenced above is as follows:
<li class="superlative"><span title="<%= superlative.name %>">
<%= superlative.body %>
</span></li>
How can I add to_sentence to the #profile.superlatives collection? I tried:
<%= render :partial => 'superlative', :collection => #profile.superlatives.to_sentence %>
Doing so however makes the #profile.superlatives disappear from the view.
I looked at the docs but couldn't find an answer.
Ohhh, now I understand. Sorry for the confusion. Here's what I would do:
In your controller:
#superlative_bodies = #profile.superlatives.map &:body
# Equivalent to: #superlative_bodies = #profile.superlatives.map {|sup| sup.body }
In your view:
= #superlative_bodies.to_sentence
Some people would do this all in the view instead, which is up to you:
= #profile.superlatives.map(&:body).to_sentence
To explain, .map is a super-useful Ruby method that takes an array or other Enumerable and a block and returns a new array where each element is the corresponding element from the original array after the block has been applied to it. For example:
[ 'foo', 'bar', 'baz' ].map {|word| word.upcase } # => [ 'FOO', 'BAR', 'BAZ' ]
# or
[ 'foo', 'bar', 'baz' ].map &:upcase # => [ 'FOO', 'BAR', 'BAZ' ]
(The latter is just a shortened version of the former for when you only want to call the same single method on every element.)
Something like this, perhaps?
module ProfilesHelper
# ...
def superlatives_items (profile)
##acb ||= ActionController::Base.new # required to access render_to_string
profile.superlatives.collect |superlative|
acb.render_to_string :partial => 'path/to/partial/superlative',
:layout => false,
:locals => { :superlative => superlative }
end
end
# ...
end
# In view:
# <%= raw(superlatives_items(#profile).to_sentence) %>
I've got a custom will_paginate renderer that overrides WillPaginate::ViewHelpers::LinkRenderer's link method like so:
def link(text, target, attributes = {})
"<a href='/users/95/friends_widget?page=#{target}' rel='next' data-remote='true'>#{text}</a>"
end
...and that works great, except you can see the hard-coded 95 in that link. How would I pass a parameter (e.g. user or user's ID) into the custom renderer via the Rails view?
<%= will_paginate(#user_friends, :remote => true, :renderer => FriendsRenderer) %>
Or is there something I'm missing, some easier way to do it?
BTW: #user_friends isn't available in the custom renderer, and I've already tried just adding params onto the end of that will_paginate call, e.g. :user => user)
will_paginate lets you pass in :params for the links:
will_paginate(#user_friends, :params => { :user_id => 95 })
View:
<%= will_paginate #user_friends, :renderer => 'FriendsRenderer',
:remote => true,
:link_path => friends_widget_user_path(#user) %>
class FriendsRenderer < WillPaginate::LinkRenderer
def prepare(collection, options, template)
#link_path = options.delete(:link_path)
super
end
protected
def link(page, text, attributes = {})
# Here you can use #link_path
end
end
Note that this works for the will-paginate version: 2.3.6
# encoding: utf-8
class UsersController < ApplicationController
def index
#search = User.search(params[:search])
#users = #search.paginate :per_page => 20, :page => params[:page]
end
end
<h2>User search</h2>
<%= form_for #search, :url => users_path, :html => { :method => :get } do |f| %>
#some form elements
<% end %>
<% #users.each do |user| %>
# show user info
<% end %>
Now how to test view with rspec 2?
# encoding: utf-8
require 'spec_helper'
describe "users/index.html.erb" do
before(:each) do
#####user = stub_model(User)
######User.stub!(:search).and_return(#post)
How to mock? If not mock(or stubed), it will got a nil error when rspec test.
end
it "renders a list of users" do
render
rendered.should contain("User search")
end
end
it "renders a list of users" do
assign(:search, stub_model(???)) # see note
assign(:users, [stub_model(User)]
render
rendered.should contain("User search")
end
assign allows the spec to refer to the instance variables that would be expected in a normal view. #users needs to be an array, thus the brackets.
Note: replace the question marks with whatever type of object gets returned from User.search.
EDIT
Well this is trickier than it appears at first blush. I could not find an easy way to mock up an object that can respond to the necessary messages to get this spec to pass. The quick and dirty way is to just use a real object:
it "renders a list of users" do
assign(:search, User.search)
assign(:users, [stub_model(User)]
render
rendered.should contain("User search")
end
The disadvantage is that this needs a database connection to work. We could hack up our own helper object:
class MetaSearchTestHelper
extend ActiveModel::Naming
include ActiveModel::Conversion
attr_accessor :model_name
def initialize(options={})
#model_name = options[:model_name] || nil
end
def singular
#model_name ? #model_name.downcase : nil
end
def persisted?
false
end
end
it "renders a list of users" do
assign(:search, MetaSearchTestHelper.new(:model_name=>"User")
assign(:users, [stub_model(User)]
render
rendered.should contain("User search")
end
Which works for this spec -- but will it work for others?
I'm looking for a best practice/standard pattern for generating feeds in Rails 3. Is http://railscasts.com/episodes/87-generating-rss-feeds still valid?
First of all, nowadays I recommend using an ATOM feed instead of RSS.
The specification of ATOM feed offers more value than the RSS one with internationalization, content types and other things and every modern feed reader supports it.
More info about ATOM vs RSS can be found at:
the Wikipedia ATOM entry
PRO Blogger and Free Marketing Zone blog posts about the subject
On to the coding:
This example assumes:
a model called NewsItem with the following attributes:
title
content
author_name
a controller for that model (news_items_controller.rb), to which you'll add the feed action
We'll use a builder template for this and the Ruby on Rails atom_feed helper which is of great use.
1. Add the action to the controller
Go to app/controllers/news_items_controller.rb and add:
def feed
# this will be the name of the feed displayed on the feed reader
#title = "FEED title"
# the news items
#news_items = NewsItem.order("updated_at desc")
# this will be our Feed's update timestamp
#updated = #news_items.first.updated_at unless #news_items.empty?
respond_to do |format|
format.atom { render :layout => false }
# we want the RSS feed to redirect permanently to the ATOM feed
format.rss { redirect_to feed_path(:format => :atom), :status => :moved_permanently }
end
end
2. Setup your builder template
Now let's add the template to build the feed.
Go to app/views/news_items/feed.atom.builder and add:
atom_feed :language => 'en-US' do |feed|
feed.title #title
feed.updated #updated
#news_items.each do |item|
next if item.updated_at.blank?
feed.entry( item ) do |entry|
entry.url news_item_url(item)
entry.title item.title
entry.content item.content, :type => 'html'
# the strftime is needed to work with Google Reader.
entry.updated(item.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
entry.author do |author|
author.name entry.author_name
end
end
end
end
3. Wire it up with a route
Let's make the feeds available at http://domain.example/feed
This will call the action with the ATOM format by default and redirect /feed.rss to /feed.atom.
Go to config/routes.rb and add:
resources :news_items
match '/feed' => 'news_items#feed',
:as => :feed,
:defaults => { :format => 'atom' }
4. Add the link to ATOM and RSS feeds on the layout
Finally, all that is left is to add the feed to the layout.
Go to app/views/layouts/application.html.erb and add this your <head></head> section:
<%= auto_discovery_link_tag :atom, "/feed" %>
<%= auto_discovery_link_tag :rss, "/feed.rss" %>
I did something similar but without creating a new action.
index.atom.builder
atom_feed :language => 'en-US' do |feed|
feed.title "Articles"
feed.updated Time.now
#articles.each do |item|
next if item.published_at.blank?
feed.entry( item ) do |entry|
entry.url article_url(item)
entry.title item.title
entry.content item.content, :type => 'html'
# the strftime is needed to work with Google Reader.
entry.updated(item.published_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
entry.author item.user.handle
end
end
end
You don't need to do anything special in the controller unless you have some special code like i did. For example I'm using the will_paginate gem and for the atom feed I don't want it to paginate so I did this to avoid that.
controller
def index
if current_user && current_user.admin?
#articles = Article.paginate :page => params[:page], :order => 'created_at DESC'
else
respond_to do |format|
format.html { #articles = Article.published.paginate :page => params[:page], :order => 'published_at DESC' }
format.atom { #articles = Article.published }
end
end
end