Draper is a great gem allowing for the Decorator pattern to easily be applied to ActiveRecord models.
When integrating Draper into an existing application, a decorator or collection of decorators must be retrieved/built instead of ActiveRecord models in order to use the functionality of your decorators (the decorators include all functionality of the ActiveRecord model they’re decorating). Draper’s README explains three methods for doing this from within the controller:
Call .new and pass in the object to be wrapped
ArticleDecorator.new(Article.find(params[:id]))
Call .decorate and pass in an object or collection of objects to be wrapped:
ArticleDecorator.decorate(Article.first) # Returns one instance of ArticleDecorator
ArticleDecorator.decorate(Article.all) # Returns an array of ArticleDecorator instances
Call .find to do automatically do a lookup on the decorates class:
ArticleDecorator.find(1)
I’d like to decorate ActiveRecord models from the view directly, leaving the controllers unchanged (directly fetching the record(s) through ActiveRecord). I use the following decorate method in app/helpers/application_helper.rb.
def decorate(records)
collection = [records] if !objects.is_a? Array
return records unless collection.first.class.name.match(/Decorator$/).nil?
klass = (collection.first.class.name + 'Decorator').constantize
decorated_objects = []
collection.each do |object|
decorated_records.push(klass.decorate(object))
end
return decorated_records if records.is_a? Array
decorated_records.first
end
Now I can simply wrap any record or collection with decorate in any view to leverage the decorator functionality.
Given a partial query such as Jas D I recently needed to search for matches in a users table across multiple fields:
:email):username):first_name):last_name)I also wanted this same search to be reusable; flexible enough to let me specify other columns within the User model to search by, such as just the first_name and last_name fields.
The requirements were simple:
jas, d) must be found within 1+ fields for a match to be successfulSqueel is an awesome gem by Ernie that provides a nice DSL for ActiveRecord. The closest example in the docs to meet my requirements is through the user of the like_any predicate method.
User.where{(first_name.like_any words) | (last_name.like_any words) | (email.like_any words) | (username.like_any words)}
There are two issues with this:
I had initially hoped there was some combination of parentheses, |’s and &’s I could use to generate a query that met my requirements, but quickly realized I needed to work with the internals of Squeel directly (I could not see a way to specify colum names dynamcially through the provided DSL).
The method must be a class method
def self.live_search(words, columns)
# …
end
To meet my requirements, the method must
OR (|) from the DSLOR‘d expressions with a boolean AND (&)where{}Or in code
def self.live_search(words, columns)
words.map do |word|
columns.map do |column|
Squeel::Nodes::Predicate.new(Squeel::Nodes::Stub.new(column), :matches, word)
end.inject do |t, expr|
t | expr
end
end.inject do |t, expr|
t & expr
end.tap do |block|
return where{block}
end
end
Note: The use of the loops, |, & and :matches predicate method are specific to my requirements. This should provide enough of a guide though to build a dynamic Squeel DSL block yourself based on your own requirements.
The above method in the User model can be used from a controller as follows
query = params[:query].downcase
stop_words = ['mr', 'mrs', 'ms', 'miss']
# Allows apostrophe for last names like O'Donnel
query_words = (query.downcase.split(/[^\w\']+/) - stop_words).map{ |w| "%#{w}%" }
results = User.active.live_search(query_words, [:first_name, :last_name, :email, :username])
and in other instances of the live-search where :email or :username are irrelevant
results = User.active.live_search(query_words, [:first_name, :last_name])
Strings passed will be formatted to obey English title rules.
"HAPPY BIRTHDAY".proper_titleize #=> Happy Birthday
"OvEr THE MouNTaiN And ThrOuGH thE WooDS".proper_titleize #=> Over the Mountain and Through the Woods
A link to the gist can be found below the source.
class String
# Converts a string into a properly capitalized English title
#
# @param [String] string the string to titleize
#
# @return [String] the titleized string
def proper_titleize
words = self.downcase.split(/\s+/)
combination_exceptions = [
'even though', 'so that', 'even if', # subordinating conjunctions
'to do', 'to be' # infinitives
]
exceptions = %w( because if after when although while since ) # subordinating conjunctions
exceptions += %w( the a an ) # articles
exceptions += %w( to do ) # infinitives
exceptions += %w( nor but or yet so both and either neither not
of in is for on with as by at from
amid anti as down into like minus near off
onto over past per plus save than up upon via ) # adposition
title = []
i = 0
while i < words.length do
if i == 0 or i == words.length-1 or words[i].upcase === words[i]
title.push(words[i].capitalize)
elsif combination_exceptions.include?("%s %s" % [words[i], words[i+1]])
title += [words[i], words[i+1]]
i += 1
elsif !exceptions.include?(words[i])
title.push(words[i].capitalize)
else
title.push(words[i])
end
i += 1
end
return title.join(' ').strip
end
end
(Source: gist.github.com)
Google used to offer a SOAP API for spelling suggestion/correction but put it out of service in November 2010. Since then the only way I had found to reliably get Google’s recommended spelling suggestion for an incorrectly spelled phrase was through the same interface their toolbar browser extension uses to help correct spelling mistakes. My SpellCheck project is a PHP5 tool that asks Google it’s suggestion for a given phrase the same way their own toolbar does and based on the returned response, the original phrase will be parsed and updated to reflect the recommended changes.
The Google toolbar sends and HTTP POST request to https://www.google.com/tbproxy/spell?lang=en&hl=en originally containing an XML body as shown below (Note: appls and ornages is the phrase being queried).
<?xml version="1.0" encoding="utf-8" ?>
<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">
<text>appls and ornages</text>
</spellrequest>
The returned response would come as XML too, with a body something like
<?xml version="1.0" encoding="utf-8" ?>
<suggestions>
<c o="0" l="5">apples\tapple\tapps</c>
<c o="10" l="7">oranges\torange</c>
</suggestions>
Each c node contains an o attribute which is the starting point of a word to be replaced and an l attribute which is the length of the original word to be replaced. The text content of each c node is a tab-delimited list of suggestions in order of it’s potential to be what you really meant to type.
Google’s HTTP responses no longer contain o or l attributes, suggesting their toolbar does a bit more work to determine where replacements should be made based on the suggestions returned.
Rewriting an application in Ruby I needed to find a different way to get suggestions from Google. Using Nokogiri’s CSS selector support, This proved to be trivial.
require 'open-uri'
require 'nokogiri'
require 'awesome_print'
query = CGI::escape(ARGV)
doc = Nokogiri::HTML(open("http://www.google.com/search?q=#{query}"))
nodes = doc.css('#topstuff p a')
ap nodes[0].content if nodes.length > 0
(Note: You can put this in spellsuggest.rb and run ruby spell_suggest.rb "appls and ornages")
The main Google search results page is queried and parsed instead of working with the Google toolbar. The added benefit of this solution is that the suggestions seem to account for context better than the Google toolbar’s. Google’s toolbar suggestions seem to inspect and process each word independently, whereas main search page accounts for the entire phrase. A word which alone might be considered incorrectly spelled may make perfect sense in context (for example, my last name Daly might come back with suggestion Daily when the query is Jason Daly, however the Ruby solution leveraging Google’s main search page returns no suggestion).
This has an admittedly very specific use-case, but recently I needed to validate strings matching the following formats:
[apples, oranges]
[[apples, oranges]]
[apples, oranges, [peaches, grapes]]
The bracket nesting is important as it will later be parsed by a YAML parser to generate nested arrays of strings
[
"apples",
"oranges",
[
"peaches",
"grapes"
]
]
and then converted to SQL conditions
... WHERE tag1.tag ILIKE "apples" OR tag2.tag ILIKE "oranges" OR (tag3.tag ILIKE "peaches" AND tag4.tag ILIKE "grapes")
The user needed to be prompted as they were entering the strings in this unique format to let them know whether or not their string was valid.
validateTagFormat = function(string){
var b = 0;
for (i=0; i<string.length; i++) {
var c = string[i];
switch (c) {
case '[':
b++;
break;
case ']':
b--;
break;
}
}
if (b != 0) return false;
if (string.indexOf('[') > string.indexOf(',') && string.indexOf(',') != -1) return false;
if (string.indexOf(',') > -1 && string.indexOf('[') == -1) return false;
if (/\]\s*[^\],]/.test(string)) return false;
string = string.replace(/[\[\]]/g, '');
string = string.replace(/,\s*/g, ', ');
return /^[a-zA-Z0\-\ ]+((\,\ )[a-zA-Z0\-\ ]+)*$/.test(string);
};
This jsFiddle instance shows this in action.
Recently needed to apply a keyup event to a series of textfields during MooTools domready event, but only have each field’s event execute when that field has the user’s focus.
/**
* Custom MooTools event, restricting firing of the keyUp event to only when the target element has focus.
*
* Usage:
* some_element.addEvent('focusedKeyUp', function(event) {
* console.debug('A key was pressed while the following element has focus: ' + event.target.get('id'));
* });
*/
Element.Events.focusedKeyUp = {
base: 'keyup',
condition: function(event) {
return event.target == $$(event.target.get('tag') + ':focus')[0];
}
};
(Source: gist.github.com)
In config/routes.rb if routes such as the following exist:
match "users/customers" => "users#customers", :as => :users_customers, :via => :get
match "users/employees" => "users#employees", :as => :users_employees, :via => :get
match "users/:id/toggle-status" => "users#toggle_status", :as => :users_toggle_status, :via => :put
the views associated with the :users_customers and :users_employees actions above (in this case each displaying a list-view of users of the specified type) likely both have a link referencing the :users_toggle_status route.
Within the toggle_status action the route to redirect back to can be determined by looking at the type of user who’s status is being toggled. A sample version of this action is below.
def toggle_status
user = User.find(params[:id])
user.toggle_status!
if user.type == 'employee'
redirect_to :users_employees and return
end
redirect_to :users_customers and return
end
The condition above and multiple redirect_to’s is cumbersome and unnecessary. Instead, we can use Ruby’s intern method for strings.
def toggle_status
user = User.find(params[:id])
user.toggle_status!
redirect_to ('users_%s' % user.type).intern and return
end
When displaying a threaded conversation with messages paginated by Kaminari, I had the unique requirement to display messages in DESC order by their creation date. This meant that if there were 12 messages with 5 per page, the breakdown would be
12 - 87 - 32 - 1To actually display the message number next to each message, it had to be calculated using the methods added to a paginated model by Kaminari (see the :message_number value below)
<div class="comments">
<% @messages.each_with_index do |message, index| %>
<%= render :partial => 'message', :locals => {:message => message, :message_number => @messages.total_count - (@messages.current_page - 1) * @messages.limit_value - index} %>
<% end %>
</div>
For now this is the only place the above calculation is relevant. Should I need it in multiple places I would move the above calculation to a helper.
Recently working with Kaminari, I wanted to have text display next to the pagination links like
283 Items - Page 3/15
I wanted the Items text to be dynamic according to the model the paginated collection was representing. Looking over my model names I found converting each to a human readable version would work great in all cases I needed a paginator. Simply using pluralize (I use singular form for my models) will not work in all cases.
User would become Users - great!Page would become Pages - great!ConversationMessage would become ConversationMessages - yuck!For the ConversationMessage model, I really only wanted to display the items in the collection to the user as Messages, not ConversationMessages or even Conversation Messages. I found this was common for my other models on the many side of a one-to-many relationship too (ie. for my UserComplaint model, I wanted the pagination text to say simply Complaints).
The following works beautifully for all models I currently have in place
@collection.first.class.name.underscore.humanize.pluralize.titleize[/\s?([^\s]+)$/, 1]
With models having their default names (in the plural form), the .pluralize call can be removed.
Amazon S3 is a cost-effective, redundant, and straight-forward solution for off-site data backups. Using the highly recommended aws command line tool by Tim Kay and a simple shell script running nightly, creating an automatic remote database backup is easy.
After installing aws, I created a shell script as shown below.
#!/bin/bash
export AWS_ACCESS_KEY_ID= # your aws access key id
export AWS_SECRET_ACCESS_KEY= # your secret access key
DBUSER= # the database username
DBPASS= # the database password
DBNAME= # the name of the database
BUCKET= # the root bucket to store the backup
FILE=$DBNAME.bkp.$(date +_%b_%d_%y.%s).sql.gz
mysqldump -u $DBUSER -p$DBPASS $DBNAME | gzip -9 > /tmp/$FILE
aws put $BUCKET/bkp/sql/$FILE /tmp/$FILE --private
rm -f /tmp/$FILE # cleanup
A nightly (at 1AM) cron is then setup for the user running the above script
0 1 * * * /home/app/s3dbbkp 2>&1
This is clearly a very simple implementation, but for my current needs (and likely others) it is perfect.
Though part of the HTML5 Specification, the activeElement attribute is now supported by all major browsers and offers the ability to determine whether an element currently has the user’s focus very easily.
Previously making the determination for which input element currently had focus was done using multiple event handlers applied to all input elements. (Note: The selector below does not include form inputs not based on the <input> tag such as a <select> list)
var current_focus; // Maintains a reference to the currently-focused input
$(document.body).getElements('input').each(function(f){
$(f).addEvents({
'focus': function(){
current_focus = $(this);
},
'blur': function(){
current_focus = false;
}
});
});
//... and later
if (current_focus == $('element-in-question')) {
// The element in question currently has the user's focus
}
Now that most major browsers support activeElement, the test for an input having focus can be done without any event handlers.
if ($(document.activeElement) == $('element-in-question')) {
// The element in question currently has the user's focus
}
Ideally your application’s code will include the old version as a fallback along with the above solution to accommodate all browsers.
(Note: This technique can be applied anywhere. This article is specifically related to a Symfony 1.4 implementation)
As recommended in the Google Page Speed best practices, static resources should be cached locally in the browser. This means setting a far-future expiration date on filetypes such as .css and .js content. This first step is achieved rather simply by adding something like below to the application’s .htaccess file or virtualhost’s httpd.conf include.
<IfModule mod_expires.c>
Header set cache-control: public
ExpiresActive on
ExpiresDefault "access plus 1 month"
# Other ExpiresBy... declarations here...
# css and javascript
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
</IfModule>
FileETag None
Note the last line above removes ETags, since according to Yahoo ETags are not needed for static content with far-future expiries set1.
The changes above will cause CSS and Javascript files to be cached locally in the browser for 1 month from the first access time, causing future requests to be made without checking against the server for newer versions of these static resources. Though the caching desired is now in place, it’s not perfect since if these static resources are modified in any way, the cached local browser version of these files will not be invalidated.
Currently in the <head> of my application, default stylesheets are added and then all included stylesheets (including those added by the specific action being requested) have their <link ... /> tags constructed and appended to the DOM using the following code
use_stylesheet('main.css');
use_stylesheet('handheld.css', '', array('media' => 'handheld'));
include_autoversioned('stylesheets');
The include_autoversioned() function is where the work is done which invalidates the browsers’ cache. This function is a wrapper of Symfony’s include_stylesheets() and include_javascripts(). I have added a Template.php file to my application’s lib/helpers/ directory which contains the function as shown below.
/**
* Depending on the type of include requested (stylesheets or javascripts), for
* each file already added to the response object, for each file, i.e. /css/base.css,
* replaces it with a string containing the file's mtime, i.e. /css/base.1221534296.css.
*
* @see http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
*
* @param $file The file to be loaded. Must be an absolute path (i.e.
* starting with slash)
*
* @throws InvalidArgumentException when invalid type is requested
*
* @return void (outputs string response directly)
*/
function include_autoversioned($type = 'stylesheets'){
if (!in_array(strtolower($type), array('stylesheets', 'javascripts'))) {
throw new InvalidArgumentException(sprintf("\$type can only be 'stylesheets' or 'javascripts': '%s' was passed", $type));
}
$function = sprintf('get_%s', $type);
$code = $function();
unset($function);
$code = preg_replace_callback('/(href|src)\=\"([^\"]+)\"/', function($matches){
if (strpos($matches[2], '/') !== 0 || !file_exists(sfConfig::get('sf_web_dir') . DIRECTORY_SEPARATOR . $matches[2])) {
$path = $matches[2];
} else {
$mtime = filemtime(sfConfig::get('sf_web_dir') . DIRECTORY_SEPARATOR . $matches[2]);
$path = preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $matches[2]);
}
return str_replace($matches[2], $path, $matches[0]);
}, $code);
echo $code;
}
(Note: The above code requires >= PHP5.3 due to the use of an anonymous function)
Also, again to the application’s .htaccess file or virtualhost’s httpd.conf include, the following must be added above the other RewriteRules that are in place for Symfony applications by default2.
# Cache-busing js and css files
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]
To show what this does, let’s look at the <link ... /> tags generated using Symfony’s get_stylesheets in the <head>
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="handheld" href="/css/handheld.css" />
and compare this to the output generated by the new include_autoversioned('stylesheets')
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.1282090705.css" />
<link rel="stylesheet" type="text/css" media="handheld" href="/css/handheld.1282076131.css" />
By modifying the filenames of these static resources by injecting a unix timestamp for the last-modified time of each file, whenever a change is made to one of these files, however small, it will cause the filename of that resource to change, effectively invalidating the local browser’s copy of that file. This guarantees that a browser will maintain a copy of a static resource for as long as can be reasonably expected, while always guaranteeing they immediately receive the latest changes to those resources as soon as they’re published.
The .htaccess section was in part taken from http://github.com/paulirish/html5-boilerplate ↩
Though modified considerably, the cache invalidation was inspired by this stackoverflow answer ↩
After implementing query caching using Doctrine’s Doctrine_Cache_Apc interface in a Symfony application I am working on, when running the application’s functional tests, warnings were returned intermittently in a few places
$ [apc-warning] Potential cache slam averted for key ...
In APC >= 3.0, an apc.slam_defense configuration option was added in attempt to avoid repeated writes to the same APC cache key as might occur under very high traffic. This apc.slam_defense option was later removed due to having been deprecated in favor of apc.write_lock as of APC >= 3.0.11. This is enabled by default, so the first thing I tried in my development environment was disabling this option. A simple test case will still fail though with apc.write_lock disabled.
apc_store('my_key', 1);
apc_store('my_key', 2);
echo apc_fetch('my_key'); // outputs 1, not 2
One of the more recent comments in this PECL ticket for APC offers a patch that can be applied to the latest release of APC before compiling it. This patch re-introduces the previously removed apc.slam_defense option. For my development environment only (since the cache slam warnings thrown in this environment due to my functional tests are irrelevant), I fixed the cache slam warnings by following these steps
php.ini, add the newly recognized apc.slam_defense=0 With apc.slam_defense in place and disabled, the test case above and my application’s functional tests run without any cache slam warnings.
There may be times when part or all of a text/plaintext version of an action’s template needs to be rendered for use from within another action who’s display format is set as text/html. For example, if there is a text/plaintext version of an order receipt that appears within an application, and that same version of the order receipt needs to be mailed to a user from within a separate action, we need to tell the order receipt to render in text/plaintext otherwise it will attempt to render in the format of the current action; in this case text/html. This is assuming the email is being sent as text/plaintext itself and the rendering action’s format is text/html.
Symfony offers a sfWebRequest::getPresentationFor() method which provides the functionality to render an action’s view from within another action, even if the calling action resides in a separate module. The trick is specifying the format for the other action while maintaining the requested format for the current action. The getPresentationFor() method signature is limiting in it’s support for specifying a format, so the only option appears to be as follows.
// In the current action
$format = sfContext::getInstance()->getRequest()->getRequestFormat();
sfContext::getInstance()->getRequest()->setRequestFormat('txt');
$otherActionContent = sfContext::getInstance()->getController()->getPresentationFor('moduleName', 'otherActionName');
sfContext::getInstance()->getRequest()->setRequestFormat($format);
unset($format);
The above code
Doctrine provides a stock Sluggable template behavior that can be applied to models, generating a slug based on one or more columns for a Doctrine_Record instance.
In most cases it is best practice not to modify a slug after it is generated, since one or more external sources may be referencing a URL to the Doctrine_Record instance by the original slug. If the slug is modified, those URLs referencing the original slug will become broken. For this reason, Doctrine’s default behavior is to have the canUpdate option for the Sluggable template be set to false.
There may however be reason to override this disabled canUpdate behavior under certain conditions. Doctrine 1.2 does not seem to provide an easy way to access a specific Doctrine_Record_Listener and modify an option associated with that listener for a given Doctrine_Record instance. Assuming there is only a single instance of any given listener associated with a Doctrine_Record, it’s fairly straightforward to find the desired listener within the Doctrine_Record_Listener_Chain. Applying this search conditionally to a Doctrine_Record::preUpdate() hook would look something like is shown below.
// In your Doctrine_Record class
public function preUpdate($event){
if (true) { // Conditions to check against for whether or not to allow the slug to be modified go here
try {
SomeUtilityClass::getListener($event->getInvoker()->getListener(), 'Sluggable')->setOption('canUpdate', true);
} catch (Exception $e) {
// Silently fail
}
}
}
// In SomeUtilityClass
class SomeUtilityClass {
static public function getListener(Doctrine_Record_Listener_Chain $chain, $search) {
$i = 0;
do {
if ($chain[$i] == null) {
throw new InvalidArgumentException('The requested Doctrine_Record_Listener is not associated with this Doctrine_Record instance');
}
if (get_class($chain[$i]) === sprintf('Doctrine_Template_Listener_%s', $search)) {
return $chain[$i];
}
} while (++$i);
}
}
The above getListener() method is required since it appears the only access to Doctrine_Record_Listener is via an implementation of PHP’s predefined ArrayAccess interface. Since the Doctrine_Record_Listener_Chain class only implements the set() and get() methods from the ArrayAccess interface, the only way to access the listeners is via their automatically assigned numeric keys. There is no way to directly request a specific listener by name through the Doctrine 1.2 API, thus the necessity for the do...while loop.