A simple negative captcha for Rails.
A negative captcha flips the normal captcha on its head, rather than asking humans identify themselves, we trick the bots into identifying themselves. We do this by placing honey pots in a form that are invisible to a human, but visible to a bot. When the bot submits the form, we look for the honey pot entries and discard the form submission if we find any.
The source is MIT licensed and available on github.
Installing Bouncy Bots!
Bouncy Bots! is available as a gem from github. Install the gem with the following command:
% sudo gem install --source http://gems.github.com/ leshill-bouncy_bots
After you have the gem installed, add a config.gem line to your environment.rb file:
1 config.gem 'leshill-bouncy_bots', :lib => 'bouncy_bots', :version => 0.1.0
Using Bouncy Bots!
Controllers
Use the bounce_bots macro in your controllers to detect and bounce bots. The macro takes two parameters, the honey pot field name and the redirect path or url. For example, to check for the field :blog_url and redirect bots to the pages_path :
1 bounce_bots :blog_url, :pages_path
You can also pass the standard controller filter options such as :only or :except :
1 bounce_bots :blog_url, :pages_path, :only => [:create, :update]
Views
In your form views, add the honey pot field. If you are using form_tag, you can use any form element, for example (using haml):
1 = text_field_tag :blog_url, nil, :class => 'long_required'
In your stylesheet, add a rule to ‘hide’ the field:
1 input.long_required { display:none; }
If you use form_for, there are two helpers to simplify making the honey pot.
1 = f.bounce_label 'Blog Url', :class => 'long_required'
2 %br
3 = f.bounce_field :class => 'long_required'
And that’s it.
will_paginate using ul and li tags
will_paginate is the defacto standard plugin for the Rails community. Almost every app will need pagination and will_paginate does almost everything you need.
The one thing will_paginate does not do is generate semantic pagination markup. Semantic markup uses standard HTML tags to convey structure and meaning. For example using an h1 tag for a heading rather than styling a div tag to do the same.
The default will_paginate pagination is a series of anchors and spans strung together. As many a designer has pointed out, that series is really a list of pagination controls. If you are working with such a designer or you are writing semantic markup, try out my fork and enjoy will_paginate with semantic pagination.
How to install
As of this moment, you can install it as a plugin. I am considering releasing this as a gem; ideally the patch to will_paginate would be accepted (see below).
%script/plugin install git://github.com/leshill/will_paginate.git
How to use
Use the will_paginate view helper, passing the :semantic => true option. If you do not want the enclosing ul tag, pass :container => false.
<%= will_paginate @pages, :semantic => true %>
Which looks like:
1 <ul class="pagination">
2 <li class="disabled prev_page">« Previous</li>
3 <li class="current">1</li>
4 <li><a href="/pages?page=2" rel="next">2</a></li>
5 <li><a href="/pages?page=3">3</a></li>
6 <li><a href="/pages?page=4">4</a></li>
7 <li><a href="/pages?page=5">5</a></li>
8 <li><a href="/pages?page=6">6</a></li>
9 <li><a href="/pages?page=7">7</a></li>
10 <li><a href="/pages?page=8">8</a></li>
11 <li><a href="/pages?page=9">9</a></li>
12 <li class="gap">…</li>
13 <li><a href="/pages?page=33">33</a></li>
14 <li><a href="/pages?page=34">34</a></li>
15 <li class="next_page"><a href="/pages?page=2" class="next_page" rel="next">Next »</a></li>
16 </ul>
Will this patch be accepted?
Signs point to ‘uncertain’. Mislav has in the past indicated that he was not convinced that list support was necessary in the plugin since you can replace the renderer. Since semantic markup is commonly used, I have sent a pull request. In any event, the authors of will_paginate have my thanks for creating a very useful plugin.
Ruby and the argument collecting hash syntax
Unlike other languages, ruby does not support keyword parameters. As an alternative, we use ruby’s syntactic shorthand to pass a hash as the final argument to a method using the key => value syntax:
1 some_method(first_arg, :key1 => value1, :key2 => value2)
some_method will receive a hash as its second argument with the specified key/value pairs. Unlike regular arguments, there is no way to provide default values for the individual key/value pairs (you can provide a default for the hash argument though):
1 def some_method(thing, options = {})
2 #...
3 end
There are a few ruby idioms for providing default options to an option hash, one of the most common is available in rails, using the core extension Hash#reverse_merge from ActiveSupport:
1 def some_method(thing, options = {})
2 options.reverse_merge!({:key1 => 'default'})
3 #...
4 end
If you do not have access to ActiveSupport, you can use ruby’s Hash#merge as an alternative:
1 def some_method(thing, options = {})
2 options = {:key1 => 'default'}.merge(options)
3 #...
4 end
If that seems like too much code to be typing all the time, you can open up Hash and add a method. Careful! This is simple enough to get wrong. Here is some code from a very useful rails plugin:
1 def default!(defaults = {})
2 defaults.each do |key, value|
3 self[key] = value if self[key].nil?
4 end
5 self
6 end
This code works until you want to set a default value to nil. This can be fixed by replacing the conditional on line three with unless self.has_key?(key). Or you can simply rewrite this in terms of Hash#merge:
1 def default!(defaults = {})
2 replace(defaults.merge(self))
3 end
We recently needed to show a truncated version of existing HTML content. Although there are several issues when dealing with HTML content, our specific concern was maintaining the integrity of the HTML. Some quick googling led to a nice helper written by Henrik Nyh last year. We tweaked the original a bit to append the ellipsis within the tag at the truncation point and truncate at a word (or tag) boundary. Here it is, enjoy.
1 # By Henrik Nyh <http://henrik.nyh.se> 2008-01-30.
2 # Free to modify and redistribute with credit.
3 # Word truncation and fixes by Les Hill <http://blog.leshill.org> 2009-06-02
4 #
5
6 require "rubygems"
7 require "hpricot"
8
9 module TextHelper
10
11 # Like the Rails _truncate_ helper but doesn't break HTML tags or entities.
12 def truncate_html(text, max_length = 30, ellipsis = "...")
13 return if text.nil?
14 doc = Hpricot(text.to_s)
15 doc.inner_text.chars.length > max_length ? doc.truncate(max_length, ellipsis).inner_html : text.to_s
16 end
17
18 def self.truncate_at_space(text, max_length, ellipsis = '...')
19 l = [max_length - ellipsis.length, 0].max
20 stop = text.rindex(' ', l) || 0
21 (text.length > max_length ? text[0...stop] + ellipsis : text).to_s
22 end
23 end
24
25 module HpricotTruncator
26 module NodeWithChildren
27 def truncate(max_length, ellipsis)
28 return self if inner_text.chars.length <= max_length
29 truncated_node = dup
30 truncated_node.name = name
31 truncated_node.raw_attributes = raw_attributes
32 truncated_node.children = []
33 each_child do |node|
34 break if max_length <= 0
35 node_length = node.inner_text.chars.length
36 truncated_node.children << node.truncate(max_length, ellipsis)
37 max_length = max_length - node_length
38 end
39 truncated_node
40 end
41 end
42
43 module TextNode
44 def truncate(max_length, ellipsis)
45 self.content = TextHelper.truncate_at_space(content, max_length, ellipsis)
46 self
47 end
48 end
49
50 module IgnoredTag
51 def truncate(max_length, ellipsis)
52 self
53 end
54 end
55 end
56
57 Hpricot::Doc.send(:include, HpricotTruncator::NodeWithChildren)
58 Hpricot::Elem.send(:include, HpricotTruncator::NodeWithChildren)
59 Hpricot::Text.send(:include, HpricotTruncator::TextNode)
60 Hpricot::BogusETag.send(:include, HpricotTruncator::IgnoredTag)
61 Hpricot::Comment.send(:include, HpricotTruncator::IgnoredTag)