Author Archive
CSS Technique for making Skip Links useful
Skip links are links at the very top of the document with anchors to other parts of the same page, that help people using assistive technologies navigate the page faster. Traditionally, skip links were hidden away for “normal” users. The very first approach was using display: none for these links. But as assistive technology caught up, hidden areas are now hidden to everyone. So other clever techniques appeared for hiding the links from screen-based user agents, but not from, say, screen readers, like using a large negative text-indent. While this technique works fine most of the time, it seems that recent versions of VoiceOver in Safari skip these links in certain situations (particularly in combination with the float, display: block, or position properties), because they are not visible anyways. It is only a matter of time until other screen readers catch up with this, and ignore hidden skip links as well.
Now, an interesting observation is that skip links are not only useful for blind people. They are equally or even more useful for well-sighted people who need to control the browser without a mouse, and therefore exclusively use their keyboard for navigation, tabbing through the elements of the page. So the question is: How do we make skip links useful for blind users as well as users who navigate the page with their keyboards?
Our proposed solution is to show all skip links at the very top of the page, but to save space, stack them on top of each other in a way that only the topmost skip link can be seen at a time. Then, as the user is tabbing through the links, she sees (or hears) the link with focus on top of the other links.
Here is how it’s done. First, the markup:
<div id="skip-links">
<ul>
<li><a href="#content" class="first-link">Content</a></li>
<li><a href="#header">Header</a></li>
<li><a href="#sidebar">Side bar</a></li>
<li><a href="#footer">Footer</a></li>
</ul>
</div>
The CSS for the outermost div looks like this:
#skip-links {
float: left; /* if you want things to appear next to the links */
width: 10em; /* or bigger, depending on your link texts */
}
Now, the width part is important, otherwise the div gets way to wide.
The list itself has no special properties, except maybe list-style: none, which might as well come from a reset style sheet.
The CSS for the li elements looks as follows:
/* This is only needed for IE < 8 */
#skip-links li {
float: left; /* This takes the list item out of the document flow */
width: 0px; /* ... to make links appear on top of each other */
}
Note that both the float and width properties are needed for older IEs.
Finally, the CSS code for the a elements is:
#skip-links li a {
z-index: 0; /* all links on same z-index */
position: absolute; /* so that the z-index property actually works */
width: 8em; /* all should have equal width so that the front-most
element covers all other elements */
/* The rest is decorative and can be changed */
background-color: white;
padding: 0.2em 0.4em;
border: 1px dashed rgb(43, 117, 179);
color: rgb(43, 117, 179);
text-decoration: none;
}
Lastly, we should make sure that the currently focused element is on top:
#skip-links li a:active, /* To make it work in IE < 8 */
#skip-links li a:focus {
z-index: 2;
/* decorative */
border: 1px solid rgb(43, 117, 179);
}
/* make sure the first link is on top when none have focus */
#skip-links li a.first-link {
z-index: 1;
}
This is what it looks like, when tabbed through:

To see all of this in action, go to imedo.de, make sure the accessibility bar on top is visible (it should be if you have Javascript turned on and this is your first visit; otherwise, click on “Hilfestellung”) and start tabbing through the page.
[Note: In Safari on Mac OS X, tabbing is done with Alt-Tab. On Firefox, you have to turn on “tabbing through all elements” in System preferences first, pane Keyboard & Mouse. In IE, it should work out of the box simply by pressing the tab key.]
Popularity: 1% [?]
Is having valid CSS a good idea?
Certainly. But with all the features of new browsers, it would be a shame not to make use of them where appropriate.
For example, Safari and Firefox support setting the style for selected text, like this:
::selection { color: #eee !important; background-color: #111 !important; }
::-moz-selection { color: #eee !important; background-color: #111 !important; }
(The above gives selected text a very high contrast, which is a good thing for accessibility reasons.)
Another very fancy thing is using Safari’s CSS 3 animation module for smooth transitions. We are using this for our accessible “Focus mode”, which is designed to help people with ADD. In Focus mode, all content except that under the mouse is faded out. In Safari, there is a smooth transition, in other browsers there is not. The transition is not an essential part of the feature, but it is only one line of CSS, and looks really good:
.layout-element { -webkit-transition: opacity 0.25s linear; }
Now, these CSS declarations cause the W3C’s CSS validator to choke. Is this a bad thing? Probably not. That is because we as web developers are well aware which CSS properties are part of the standard, and which aren’t, and also which browsers support which, and what bugs they have. That last thing is particularly interesting: even having a perfectly standards conformant CSS does not guarantee anything in terms of rendering.
Also, every known browser simply ignores properties it doesn’t support, usually without any problem whatsoever. So, while the validator is certainly a great tool to spot syntax errors, it is not really practically useful for the validation of CSS properties.
Popularity: 1% [?]
Release: css_doc, CSS file documentation extractor
We are proud to announce the immediate availability of the first version of css_doc. It is a tool to extract Javadoc-like documentation from CSS files. It was inspired by the work from the CSSDOC guys, but is NOT a complete implementation of their proposed standard. It is, however, quite similar, so that your existing CSSDOC documentation should probably work with css_doc.
This is the first release, so it is not what one would call “feature-complete”, but it is already quite stable at the moment. This version’s features include:
- File-level documentation for each CSS file in your project.
- Possibility to divide a single file into multiple sections.
- Rule-set-level documentation (a rule set is a set of selectors, separated by commas, together with their CSS properties).
- HTML-Code examples for the usage of your CSS rules are extracted. This is useful for building a style guide.
- Generates selector, section and file index pages.
For an example, please have a look at http://opensource.imedo.de/css_doc/index.html. The documented CSS file can be found here: http://opensource.imedo.de/stylesheets/style.css. I know the design of the css_doc documentation is not pretty, but we will improve on that in a future release.
If you like css_doc and are a Ruby hacker, or if you would like to improve the default design, please fork the project on github and send us a pull request. Of course the code contains lots of tests, but you are also welcome to add more tests, especially if you find bugs.
For installation instructions, please see http://github.com/imedo/css_doc.
Popularity: 1% [?]
Hack-Free, Valid Browser Detection
There are many people who are already using this technique, but it is so incredibly useful that I thought we need to post it here as well. If you want your website to look good in more than just one browser without resorting to invalid CSS syntax error hacks, you absolutely must take a look at the CSS Browser Selector Javascript. Then, you can write selectors like this:
div.article { width: 10em; }
.ie6 div.article { width: 11.26em; }
That causes the article div to have a width of 10em in standard browsers, but a width of 11.26em in IE 6. Not only is the CSS valid all of a sudden, it is also VERY clear and understandable.
Of course, CSS-based browser detection is most useful for the IE family of browsers, which incidentally suffer most from slow Javascript execution times. This is why I propose an optimization that makes browser detection faster for the IEs, and even works without Javascript (which in total should cover about 70 percent of your users). Right after your opening and right before your closing body tags, use conditional comments to select the IE browser version, like this:
<body>
<!--[if IE 6]><div class="ie ie6 win"><![endif]-->
<!--[if IE 7]><div class="ie ie7 win"><![endif]-->
<!--[if IE 8]><div class="ie ie8 win"><![endif]-->
<!--[if !IE]><!-->
<!-- Browser detection start script for other browsers, if still needed -->
<!--<![endif]-->
Your body content goes here
<!--[if !IE]><!-->
<!-- Browser detection end script for other browsers, if still needed -->
<!--<![endif]-->
<!--[if IE]></div><![endif]-->
</body>
Popularity: 1% [?]
Moving towards a flexible layout
If you take a look around the web, you will find that most web sites out there have a fixed layout, most commonly with a width of 1000 or so pixels. Notable exceptions are Wikipedia or Google, as well as a lot of application-like web sites, like Google Mail, Yahoo Mail etc.
If you are not using Windows, you already expect a window on your desktop to either be of the correct size, such that all content fits, or to be resizeable. It is always good practice to empower the user to customize the environment as she sees fit, and now we adopted that idea on imedo.de.
But the real motivation for this move was with a certain group of people in mind: those with low screen resolutions. The main use case for displays with low resolutions is for people with an impaired sight. They usually do not have the option to make the browser window larger than, say, 800×600 pixels. So the idea is to show all of the page’s content to them, even when the browser window (scrollbar and window frame included) is only 800 pixels wide. Another goal was to do this without a horizontal scroll bar. The reason behind this is that it is particularly cumbersome to scroll horizontally, and that people with impaired vision may as well have trouble seeing the scroll bar.
The key to a flexible layout is to use percentages for horizontal sizes. That is almost as much there is to it, except for two things:
1. IE < 7 does not support the max-width property. If you want your layout to grow up to a certain width, you can not accomplish that in IE 6 in a valid fashion. It is possible to use non-standard CSS expressions, or to use Javascript to detect window resizing, but both techniques are slow and pretty unstable, so that we decided that IE <= 6 gets a fixed-width layout.
2. You want fancy things like fixed-sized columns and flexible ones. It is well documented how to do that, but there is one technique I want to share with you.
Say you have a vertical list with a couple of items, each of which has a picture on the left, and text on the right. Now, the picture has a fixed size, and you want the picture to have that size regardless of the window width. But the text on the right may grow or shrink in width, like this:
Full width:

Smaller width:

One simple and cross-browser (IE5+, Safari, Opera, Firefox) way to do this is to wrap the picture in a left-floating div, and to wrap the text in a div with the picture’s width plus some space as a left margin, like this:
<ul>
<li>
<div class="image">
<img src="/some/image"/>
</div>
<div class="text">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</li>
</ul>
The CSS to make this happen is as follows:
div.image {
float: left;
width: 4em;
}
div.text {
margin-left 5em;
}
Note that the div.text does NOT declare a width, which causes all browsers to use the maximum width available. Also, do not use any overflow property on the div.text element or any child of it, as this breaks stuff in weird ways in several browsers.
This is a very simple, stable and standards conformant solution for this problem. The nice thing is that you can use it for layout columns as well (although care must be taken for IE <7, as they tend to explode layout boxes when the content gets too large). Also, it is possible to have the fixed column on the right, or to have fixed columns on the left AND the right, and have the middle column fill the remaining space.
Popularity: 1% [?]
“with” in Ruby
Introduction
Remember the old times, where you wrote in languages like Borland Delphi? Or maybe you are a Javascript developer? Or you like Python? Or even VB.NET? Well, all four languages (and others) implement some form of the with keyword, which can save you a lot of typing.
In this post we shall develop an implementation of the with keyword in Ruby. The with keyword is quite controversial and a lot of discussion about whether it is evil or not is going on all the time. But that’s not the point here; the point is that in Ruby it is possible to extend the language in amazing ways. And after all, lots of people like the with keyword a lot, and this article is primarily for these people.
Consider this Example:
@forum_post = ForumPost.new(params[:forum_post])
@forum_post.transaction do
@forum_post.user = current_user
@forum_post.topic = @topic
@forum_post.topic.last_update = Time.now
@forum_post.topic.recalculate_page_count
@forum_post.topic.save
@forum_post.published = true
@forum_post.save
end
(Arguably, this controller code could be moved to a presenter, but let’s ignore that for the sake of illustration)
Wouldn’t it be nice to get rid of all the @forum_post references? In plain Ruby, you can already do that, although it is limited, and not completely dry:
@forum_post = ForumPost.new(params[:forum_post])
@forum_post.instance_eval do
transaction do
user = current_user
topic = @topic
topic.last_update = Time.now
topic.recalculate_page_count
topic.notify_subscribers
topic.save
published = true
save
end
end
The way it should work
Since ruby is a powerful and flexible language, we can simplify the example above even further and thus make it more readable:
with ForumPost.new(params[:forum_post]) do
transaction do
user = current_user
topic = @topic
with topic do
last_update = Time.now
recalculate_page_count
notify_subscribers
save
end
published = true
save
end
end
Notice the nested use of the with method. The semantics of the with keyword in most languages is that a method inside a with block is called for the object of the innermost with block that responds to the method. Thus, if topic responds to notify_subscribers, then that method will be called for topic. If not, there will be an attempt to call it for the ForumPost object. If that does not work out, there will be a NoMethodError.
Implementation
The code that powers the with method is surprisingly simple. Firstly, we need to define a proxy object that intercepts the method calls, and delivers it to the innermost object that responds to the method. The core of this proxy object is a stack of objects that represent the objects of the nested with calls:
class WithProxy
def self.objects
@objects ||= []
end
end
Next, we need to actually intercept method calls with method_missing:
class WithProxy
def method_missing(method, *args, &block)
WithProxy.objects.each do |object|
return object.__send__(method, *args, &block) if object.respond_to?(method)
end
# raise exception if no object responds to method
super
end
end
Note that the objects array is class-level; this is because there is only one nested set of with calls at a time (at least in single-threaded applications). The method_missing method goes through each of these objects and tests if they respond to the missing method. If so, the process is halted and the methods return value is returned.
To make the with method universally accessible, we add it to the Kernel module like this:
module Kernel
def with(object, &block)
begin
proxy = WithProxy.new
WithProxy.objects.unshift object
proxy.instance_eval &block
ensure
WithProxy.objects.shift
end
end
end
In this code, the given object will be pushed to the front of the WithProxy’s array, then the given code block is executed in the context of the object, and after that, the object is removed from the array. The ensure keyword makes the with method behave well in case of an exception.
We can test this implementation with the following code:
class Test
def initialize(string)
@string = string
end
def write_something
puts "something #{@string}"
end
def yield_block(&block)
yield
end
end
with Test.new('bla') do
with Test.new('foo') do
write_something
end
write_something
yield_block do
puts "stuff"
end
end
Ways to improve the “with” implementation
There are several ways to improve the above implementation
Removing methods from the proxy
To ensure that all methods called on the proxy eventually reach on of the proxied objects, we can use an established technique to remove all methods inherited from the Object class:
class WithProxy
self.instance_methods.each do |method|
undef_method method unless method.to_s =~ /^__|instance_eval/
end
end
We remove all methods except for the very basic __send__ and __id__ methods, as well as the instance_eval method, which we need to implement the with method.
Support a python-style with keyword
Python allows to set an alias for the object supplied to the with keyword like this:
with open("x.txt") as f:
data = f.read()
We can add this functionality using the Proc#bind method from ActiveSupport like this:
module Kernel
def with(object, &block)
begin
proxy = WithProxy.new
WithProxy.objects.unshift object
block.bind(object).call(object)
ensure
WithProxy.objects.shift
end
end
end
Now, the object supplied to the with method gets yielded to the block. We can use this feature like this:
with ForumPost.new do |f|
f.user = current_user
f.topic = @topic
f.save
end
This is especially useful if we want to distinguish between several objects of nested with calls, like in the following example:
with Test.new('bla') do |t|
with Test.new('foo') do
t.write_something
write_something
end
end
Popularity: 1% [?]
Technique for testing acts_as_* plugins
Introduction
Testing Rails plugins has always been a pain, until the dry_plugin_test_helper came along. Now there is a difficulty with testing different calls to acts_as_* methods. For example, acts_as_list can be called in different ways. First, without any arguments, which means that there is an assumption that a column position exists which determines the order of the records; Second, the column can be made explicit; Third, a scope can be given, in which the order is presented. I am sure there are even more ways to call acts_as_list.
Our Technique
To test all of these calls, you’d need at least three different ActiveRecord classes as well as database tables for them, because usually an acts_as_* call does irreversible changes to a model class, such as including modules. Traditionally, the acts_as_* calls must be done before the test is run, usually in some file called abstract_unit.rb.
To get around this limitation, we’d like to present our simple solution to this problem, where you can reuse the same ActiveRecord class with the same table for multiple calls to acts_as_* methods, without the different calls interfering with each other.
(The following code assumes you use Test::Unit. Replace the setup method with the before block if you use RSpec)
Using dry_plugin_test_helper, you always have a simple model class Article defined (if you dont use dry_plugin_test_helper, the you can easily define your own model class). For the sake of illustration, we use this Article model class in the following code. First, we add a line to the setup method to remove the Article class from the object space like this:
def setup
Object.send :remove_const, :Article if Object.const_defined?(:Article)
# ...
end
The if part is neccessary, since the first time this line is executed, Article might not exist yet.
After this line, we have to redefine Article, such that we can start using it in our test cases:
def setup
# ...
Object.const_set(:Article, Class.new(ActiveRecord::Base))
end
This initializes an empty ActiveRecord subclass and assigns it to a global constant called Article. This Article constant can now be used to test your calls to your acts_as_* method.
Example
For the following example, we use the fictious acts_as_hasselhoff plugin.
class ActsAsHasselhoffTest < Test::Unit::TestCase
def setup
Object.send :remove_const, :Article if Object.const_defined?(:Article)
Object.const_set(:Article, Class.new(ActiveRecord::Base))
end
def test_should_act_as_hasselhoff_without_parameters
Article.acts_as_hasselhoff
assert Article.looking_for?(:nothing)
end
def test_should_act_as_hasselhoff_with_explicit_parameter
Article.acts_as_hasselhoff :looking_for => :freedom
assert Article.looking_for?(:freedom)
end
end
With the setup method, the two calls to acts_as_hasselhoff do not interfere with each other, not even when the acts_as_hasselhoff includes modules to the calling class (what most acts_as_* plugins do).
As an added plus, you can even use mocha or some other mocking framework to mock parts of your calls to acts_as_hasselhoff, to make sure that the correct execution paths are chosen. Of course, then the model might not act as the real Hasselhoff, but we don’t care about that, because the next test gets a fresh new Article class that acts as nothing at all.
Making things prettier
Having these complicated and hacky metaprogramming calls in your setup method is kinda ugly. So lets take them out and put them in our test_helper.rb:
class Test::Unit::TestCase
protected
def reset_active_record_class(name)
Object.send :remove_const, name if Object.const_defined?(name)
Object.const_set(name, Class.new(ActiveRecord::Base))
end
end
Now, we can reset any ActiveRecord model to an empty class whenever we want inside our tests. The simplified setup method now looks like this:
def setup
reset_active_record_class(:Article)
end
That’s it
That’s how you can test acts_as_* plugins. Enjoy!
Popularity: 1% [?]
Iterating over large ActiveRecord tables
We have some quite large tables where we need to iterate over each element on a daily basis. Using find(:all) for this purpose can be really memory consuming, since every element must be fetched from the database and must be kept in RAM until the last iteration is over. Since we recently touched the limits of our available memory in production, we had to come up with a different way to iterate over all records. The obvious solution is to fetch, say, 1000 records at a time, like so:
count = User.count
steps = count / 1000
steps.times do |i|
User.find(:all, :limit => 1000,
ffset => i * 1000).each do |user|
# do your stuff here
end
end
This works pretty well, but since there are so many places where we iterate over large tables, we want to keep it DRY from the beginning. So here is an ActiveRecord extension that does exactly that:
class ActiveRecord::Base
def self.for_each(conditions = nil, step = 1000, &block)
c = count :conditions => conditions
(c / step).times do |i|
find(:all, :conditions => conditions,
:limit => step,
ffset => step * i).each do |model|
yield model
end
end
end
end
Usage of this code snippet is straightforward. To iterate over all users, you’d write:
User.for_each do |user|
# do stuff here
end
Conditions are also supported, so if you want to iterate over all users who joined today, you’d write:
User.for_each ['created_at > ?', Date.today.to_time] do |user|
# do stuff
end
Some things to note:
- The order clause is not supported. It would not make sense anyways, since you are iterating over every record.
- Don’t modify the table you are iterating over in ways that influence the search condition. Otherwise it could happen that records are skipped.
- When using
find(:all), the results are a snapshot of the database from the time of the query. Subsequent modifications are not taken into account, which is usually desirable. With the above approach, you must be aware that the table is queried several times, possibly with modifications from other sources (this is related to “don’t modify the table”, above)
Popularity: 1% [?]
Cleaning up the models directory
While most of the files in app/models certainly model something, there are also files that do not really belong in there, and if only so that classes with a similar structure or purpose are grouped together. Of course, I’m talking about mailers and observers here, but it could also be any kind of user-defined file types, like e.g. Liquid Drops.
So, this article is for those people, who are annoyed by their cluttered app/models directory, and possibly already use Rails Engines or some other application slicing framework (not a requirement for the following tips, but you can skip the end of this article, if you have a monolithic application).
The recipe
- The first thing you’d do is to create the directories
app/mailersandapp/observers, and, if you use Engines or the like, repeat that step for every app slice you have with mailers and/or observers. - Now for the fun part. Move all your observers and mailers into their respective directories.
- Possibly hours later (depending on the size of your code base), you should be at the point that the previous step is finished, but by now, most of your tests should fail. But don’t worry, we’ll fix that in a sec.
- In your
config/environment.rb, add your new directories you just created (hours ago) to the load paths. InsideRails::Initializer.run, enter the following code:
config.load_paths += [
:mailers,
bservers,
# Add any other file types that you might have identified
].collect { |path| "#{RAILS_ROOT}/app/#{path}" }
Again, if you use engines, you need to add another file. In config/initializers/engines.rb, paste the following code:
# Note that these are singular!
file_types = [
:mailer,
bserver,
]
Engines.mix_code_from *(file_types.collect(&:to_s))
Rails.plugins.each do |plugin|
file_types.each do |file_type|
Dependencies.load_paths << "#{plugin.directory}/app/#{file_type.to_s.pluralize}"
end
end
There is one caveat with the vanilla version of engines and mailers: For this code to work, your mailer files must end with _mailer.rb (which IMHO is a good convention anyways).
Getting observers to work with engines
Now for the engines / app slicer – only part (others can stop reading here). Your observers that are not in RAILS_ROOT/app/observers can not be found by Rails, if you list them in the Initializer in environment.rb. However, there is an easy way to fix that. Just add another custom initializer to declare your observers, and remove them from environment.rb:
config/initializers/observers.rb
ActiveRecord::Base.observers = [
:article_observer,
:user_observer,
# add more here
]
ActiveRecord::Base.instantiate_observers
By the time that this initializer gets executed, the engines initializer should already have run (remember that initializers are executed in alphabetical order, so if you name the engines initializer engines.rb and the observer initializer observer.rb, you should be fine.)
Popularity: 1% [?]
Better Rails Initializers
Recently, I ported our environment.rb to the new Rails Initializers. Over time, we added a lot of initialization code in environment.rb. In particular, there are a lot of small monkey-patches that we apply to rails, or 3rd-party plugins. So for that, the Rails Initializers are a very good fit.
Still, after I cleaned up that part, there were still things like this in environment.rb:
google_maps_api_key = case RAILS_ENV
when 'test' then "the-test-key"
when 'development' then "the-development-key"
when 'production' then "the-production-key"
end
GeoKit::Geocoders.google = google_maps_api_key
While this could arguably be handled by a YAML configuration file, unfortunately GeoKit (at least the version we use right now) does not support configuration via YAML.
Another thing we do is disable Globalize in the development environment to speed up page rendering on our local machines. So the corresponding part of the environment.rb file looked something like
if RAILS_ENV == 'production'
include Globalize
else
# In reality, this is slightly bigger, to simulate more methods
# of the Globalize API
class String; def t; self; end; end
end
Hardcoding environment conditionals is somewhat like hardcoding admin privileges to the user with ID 1. Rather, an environment should be seen as a generic container for a bunch of configuration options. So I came up with a better way of using Rails Initializers like follows:
By default, Rails loads every Ruby file in config/initializers and its subdirectories after the plugins are fully loaded. This is handlet by the Rails::Initializer#load_application_initializers method. I wrote a small monkey patch to change this behavior such that only the files in config/initializers (without subdirectories) are loaded, followed by the files in config/initializers/RAILS_ENV.
class Rails::Initializer
def load_application_initializers
["#{configuration.root_path}/config/initializers/*.rb",
"#{configuration.root_path}/config/initializers/#{RAILS_ENV}/**/*.rb"].each do |path|
Dir[path].sort.each do |initializer|
load(initializer)
end
end
end
end
With this code, our Globalize initialization is distributed across two files:
config/initializers/globalize.rb
class String
def t
self
end
end
config/initializers/production/globalize.rb
include Globalize
Note: In recent Rails versions, the original load_application_initializers method is slightly more complex, such that the following patch would be used instead (warning: I have not tried that one just yet, since we’re still porting to Rails 2.1)
class Rails::Initializer
def load_application_initializers
return unless gems_dependencies_loaded
["#{configuration.root_path}/config/initializers/*.rb",
"#{configuration.root_path}/config/initializers/#{RAILS_ENV}/**/*.rb"].each do |path|
Dir[path].sort.each do |initializer|
load(initializer)
end
end
end
end
Note that this monkey patch can not be applied as a Rails Initializer, since it patches the way Rails Initializers are loaded (though it would be awesome, if something like this would be possible; effectively patching a method while it is executed
). This means that you need to require the file containing the monkey patch from environment.rb, before you run the Rails Initializer.
Enjoy your cleaned up environment.rb and initializers.
Popularity: 1% [?]
