Upgrade to Pro — share decks privately, control downloads, hide ads and more …

love your lib directory.pdf

bostonrb
March 26, 2012
340

love your lib directory.pdf

bostonrb

March 26, 2012
Tweet

Transcript

  1. It needs organization • People don’t know what to put

    in lib • They tend to treat it like a dump Monday, March 26, 12
  2. al·lit·er·a·tion 1. the commencement of two or more stressed syllables

    of a word group with the same consonant sound Monday, March 26, 12
  3. lit·ter 1. objects strewn or scattered about; scattered rubbish Monday,

    March 26, 12 I know what you’re thinking. What I just did makes no sense what so ever. Well you know what also makes no sense?
  4. KEANU DOES NOT AGE Monday, March 26, 12 This is

    clearly not a photograph, it’s a painting which should be an indicator as to how old it actually is. Nobody gets painted anymore, this is obviously from pre-photograph technology.
  5. KEANU DOES NOT AGE Monday, March 26, 12 Paul Mounet

    is clearly Keanu’s alter-ego, a psuedonym he has used to disguise himself through the ages. My theory: the guy is a robot
  6. lit·ter 1. objects strewn or scattered about; scattered rubbish Monday,

    March 26, 12 This is how people treat their lib directory
  7. lib/ 1. Misunderstanding what a model is Monday, March 26,

    12 So here are some of the points I’m going to touch on in respect to your lib directory
  8. I’m a model, idiot All business logic goes into a

    model All models go into app/models Monday, March 26, 12 This is for the most part a good convention to stick to
  9. But wait, there’s more! Monday, March 26, 12 But apps

    grow and sometimes conventions at greenfield don’t scale well Where do we put extractions that help DRY our code? What about common object patterns, where do these go?
  10. Don’t confine yourself app/ is not restricted to assets/ controllers/,

    helpers/, mailers/, models/ Monday, March 26, 12 You have the freedom to app and remove whatever directories you want. All Rails cares about is if the constants are loaded. The directories that are generated when you create a new rails app are good common sense suggestions, not hard rules
  11. Don’t confine yourself app/ assets/ controllers/ helpers/ mailers/ models/ mixins/

    strategies/ Monday, March 26, 12 I’ve been putting my mixins and strategies into these directories, as long as you follow the underscore to camelcase convention Rails will auto require the files
  12. RUBY $LOAD_PATH Monday, March 26, 12 lib/ has special meaning

    in Ruby When you require a file such as
  13. RUBY $LOAD_PATH require ‘activerecord’ Monday, March 26, 12 Basic idea:

    ruby iterates through the load path and stops on the first match and requires that absolute path
  14. RUBY $LOAD_PATH ["~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/actionmailer-3.2.1/lib", Monday, March 26, 12 Load path from a new Rails app
  15. RUBY $LOAD_PATH ["~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/actionmailer-3.2.1/lib", Monday, March 26, 12 These are actually paths to gems!
  16. RUBY $LOAD_PATH ["~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/actionmailer-3.2.1/lib", Monday, March 26, 12 And each gem (almost) has a lib directory
  17. RUBY $LOAD_PATH ["~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/actionmailer-3.2.1/lib", Monday, March 26, 12 Here is activerecord
  18. RUBY $LOAD_PATH activerecord-3.2.1/lib/active_record.rb Monday, March 26, 12 So a misconception

    is that the gem name needs to match the require name, the gem name has nothing to do with what you are requiring at all. There could be more than one files with ‘active_record’ in the load path and Rubygems will just require the first one it happens to find... let’s demonstrate that by doing something *very* stupid
  19. # config/application.rb $:.unshift(File.expand_path('../../lib', __FILE__)) # lib/active_record.rb raise ‘ZOMG I BORKED

    RAILS!’ $ rails c Monday, March 26, 12 Not something you should do, most likely no good use case for doing this but it does a good job of demonstrating what is happening under the hood which is good to understand
  20. INITIALIZERS!! Monday, March 26, 12 Yes, initializers are auto loaded,

    and they don’t require you to conform to the file and class name convention that is required for anything in Rails’ autoload path. But just dumping any code in here is *BAD*? Why? Because you are most likely working with a team of people and there should be conventions on how your app is organized. It is important.
  21. • Extracting code from your app into a gem •

    Monkey Patching Core, Stlib, and Gems Monday, March 26, 12 *gasp* monkey patching!
  22. Monday, March 26, 12 Keanu Reeves is smiling because he

    has a secret to hide, but unfortunately he isn’t a very good actor so he can’t hide it very well. Nonetheless he doesn’t want to tell you his secret, but I will. It turns out that Keanu has been getting major cred in the Ruby community by releasing TONS of gems. How does he do it so easily? Let’s see.
  23. RUBY $LOAD_PATH ["~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/actionmailer-3.2.1/lib", Monday, March 26, 12 this
  24. RUBY $LOAD_PATH [“my_app/lib”, "~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", Monday, March 26, 12 is actually this
  25. RUBY $LOAD_PATH [“my_app/lib”, "~/.rvm/gems/ruby-1.9.2/gems/uglifier-1.2.3/lib", "~/.rvm/gems/ruby-1.9.2/gems/sqlite3-1.3.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-rails-3.2.4/lib", "~/.rvm/gems/ruby-1.9.2/gems/sass-3.1.15/lib", "~/.rvm/gems/ruby-1.9.2/gems/rails-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/pry-0.9.8.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/slop-2.4.4/lib",

    "~/.rvm/gems/ruby-1.9.2/gems/method_source-0.7.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/jquery-rails-2.0.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-rails-3.2.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/railties-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/thor-0.14.6/lib", "~/.rvm/gems/ruby-1.9.2/gems/rdoc-3.12/lib", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext/json/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/ext", "~/.rvm/gems/ruby-1.9.2/gems/json-1.6.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/rack-ssl-1.3.2/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-2.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/execjs-1.3.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coffee-script-source-1.2.0/lib", "~/.rvm/gems/ruby-1.9.2/gems/coderay-1.0.5/lib", "~/.rvm/gems/ruby-1.9.2/gems/activeresource-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/activerecord-3.2.1/lib", "~/.rvm/gems/ruby-1.9.2/gems/tzinfo-0.3.31/lib", "~/.rvm/gems/ruby-1.9.2/gems/arel-3.0.0/lib", Monday, March 26, 12 This is prepended to your load path after the environment loads up So now that our lib directory is exposed to the load path, we can treat it like the lib directory in a gem.
  26. Extracting a Gem Isolate the code Extract into lib/ Test

    in isolation Extract to a Gem Monday, March 26, 12
  27. Case Study - BostonRB • BostonRB needed Google Calendar access

    • Patrick Robertson kicked some ass • He still has not extracted into a Gem https://github.com/bostonrb/bostonrb/blob/master/lib/boston_rb_calendar.rb Monday, March 26, 12 This makes Keanu Reeves sad But that’s ok Keanu!, his code is in a great spot to be extracted into a gem. Or at the very least, for someone else to extract it and take all of the credit. Let’s take a look at what he’s done
  28. Monday, March 26, 12 because the app’s lib directory is

    in the load path Patrick can require the files under the boston_rb_calendar namespace without using absolute paths or unshifting local load path
  29. Monday, March 26, 12 Because his code is properly isolated

    he can test it in isolation as part of BostonRB’s test suite
  30. Monday, March 26, 12 I suggest someone else extract this

    code and upload it to Rubygems and take all of the credit. Immediately.
  31. Object#whoa # lib/core_ext/object.rb class Object def whoa puts “Most Excellent!”

    end end # config/initializers/lib_initializer.rb require ‘core_ext’ Monday, March 26, 12
  32. Object#whoa # lib/core_ext/object.rb class Object def whoa puts “Most Excellent!”

    end end # config/initializers/lib_initializer.rb require ‘core_ext’ # lib/core_ext.rb require ‘core_ext/object’ Monday, March 26, 12
  33. Haml Handlebars Filter # lib/gem_ext/haml.rb require ‘gem_ext/haml/custom_filters’ # lib/gem_ext/haml/custom_filters.rb module

    Haml::Filters module Handlebars ... end end # lib/gem_ext.rb require ‘gem_ext/haml’ # config/initializers/lib_initializer require ‘gem_ext’ Monday, March 26, 12
  34. Know what to put into app/ Don’t misuse initializers Do

    use lib/ as an extraction point for Gems Follow the *_ext/ convention for monkey patching Monday, March 26, 12