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

Models Models Every Where

2d643c10e432e230c9d4bf97d15d6a46?s=47 Chris Griego
February 07, 2012

Models Models Every Where

Models form the backbone of our applications, but what are they and where to they come from? More importantly, do you have enough models or is your application missing some? Chris will show you how to spot missing models as well as create models with all of the goodies you’ve come to expect from Ruby on Rails. Along the way you’ll be introduced to ActiveAttr which makes it easy to construct models.

2d643c10e432e230c9d4bf97d15d6a46?s=128

Chris Griego

February 07, 2012
Tweet

Transcript

  1. Models Models Every Where The Rime of the ActiveModel

  2. SCALE MODEL?

  3. MODEL HOME?

  4. TYRA BANKS?

  5. What are models?

  6. app/models

  7. ActiveRecord

  8. Rails + Merb = 3.0

  9. The Great Decoupling ActiveRecord ActiveResource Sequel DataMapper Mongoid MongoMapper

  10. Why app/models?

  11. MODEL VIEW CONTROLLER (MVC) SMALLTALK AT XEROX PARC (1979)

  12. GUI Architectures Model View Controller Model View Adapter Model View

    Presenter Model View ViewModel Hierarchical Model View Controller
  13. Models, models every where Entities Value Objects Request Models Response

    Models Presentation Models
  14. “Model objects are the things in the system that you

    are trying to model.” David Heinemeier Hansson, 2004
  15. But what are models?

  16. MODEL VIEW CONTROLLER (MVC) SMALLTALK AT XEROX PARC (1979)

  17. MODELS REPRESENT KNOWLEDGE MODELS = KNOWLEDGE, KNOWLEDGE = POWER, MODELS

    = POWER
  18. MODELS AREN’T DEFINED BY PERSISTENCE THE ELEPHANT IN THE ROOM

  19. Examples of Models in Rails Rack::Request Rails::Application ActiveModel::Errors ActiveResource::Connection ActiveRecord::Associations::BelongsToAssociation

  20. SPOTTING MISSING MODELS

  21. WHERE DO MODELS HIDE? UNDER YOUR NOSE

  22. MODELS HIDE IN CONTROLLERS FAT CONTROLLERS?

  23. MODELS HIDE IN VIEWS LOGIC LADEN VIEWS?

  24. MODELS HIDE IN OTHER MODELS MODELS WITH TOO MUCH RESPONSIBILITY

  25. MODELS HIDE BITS OF THEMSELVES ALL OVER SPREAD IT AROUND

  26. SIGNS TO WATCH FOR

  27. MODEL VIEW CONTROLLER VIEWS AND CONTROLLERS WITHOUT A CLEAR SOURCE

    OF KNOWLEDGE X
  28. PRIMITIVE OBSESSION USING BUILT-IN TYPES TO DO A CLASS’ JOB

  29. LARGE CLASS MULTIPLE RESPONSIBILITIES

  30. Ideas for Models Address Contact Form Web Service Request/Response Presentation/View

    Model
  31. Example LoginRequest

  32. app/views/sessions/new.html.erb <% flash.each do |name, message| %> <%= content_tag(:p, message)

    %> <% end %> <h1>Log In</h1> <%= form_tag session_path do %> <div class="field"> <%= label_tag 'email' %><br /> <%= text_field_tag 'email', @email %> </div> <div class="field"> <%= label_tag 'password' %><br/> <%= password_field_tag 'password', nil %><br /> <%= link_to 'Forgot Password?', password_help_path %> </div> <div class="field"> <%= check_box_tag 'remember_me', '1', @remember_me.nil? ? true : @remember_me %> <%= label_tag 'remember_me' %> </div> <div class="actions"> <%= submit_tag 'Log in' %> </div> <% end %>
  33. app/views/sessions/new.html.erb <% flash.each do |name, message| %> <%= content_tag(:p, message)

    %> <% end %> <h1>Log In</h1> <%= form_tag session_path do %> <%# … %> <% end %>
  34. app/views/sessions/new.html.erb <div class="field"> <%= label_tag 'email' %><br /> <%= text_field_tag

    'email', @email %> </div>
  35. app/views/sessions/new.html.erb <div class="field"> <%= label_tag 'password' %><br/> <%= password_field_tag 'password',

    nil %><br /> <%= link_to 'Forgot Password?', password_help_path %> </div>
  36. app/views/sessions/new.html.erb <div class="field"> <%= check_box_tag 'remember_me', '1', @remember_me.nil? ? true

    : @remember_me %> <%= label_tag 'remember_me' %> </div>
  37. app/views/sessions/new.html.erb <div class="actions"> <%= submit_tag 'Log in' %> </div>

  38. app/controllers/sessions_controller.rb class SessionsController < ApplicationController def new end def create

    logout_keeping_session! user = User.authenticate(params[:email], params[:password]) if user self.current_user = user handle_remember_cookie! (params[:remember_me] == "1") flash[:notice] = "Logged in successfully." redirect_back_or_default root_url else errors = [] errors << "Email can't be blank" if params[:email].blank? errors << "Password can't be blank" if params[:password].blank? errors << "Password is too short (minimum is 5 characters)" if params[:password].present? && params[:password].length < 5 flash.now[:error] = errors.any? ? errors.join(" ") : "Couldn't log you in as '#{params[:email]}'" logger.warn "Failed signin for '#{params[:email]}' at #{Time.now.utc}" @email = params[:email] @remember_me = !!params[:remember_me] render :new end end end
  39. app/controllers/sessions_controller.rb def new end

  40. app/controllers/sessions_controller.rb def create logout_keeping_session!

  41. app/controllers/sessions_controller.rb user = User.authenticate(params[:email], params[:password])

  42. app/controllers/sessions_controller.rb if user self.current_user = user handle_remember_cookie! (params[:remember_me] == "1")

    flash[:notice] = "Logged in successfully." redirect_back_or_default root_url else
  43. app/controllers/sessions_controller.rb errors = [] errors << "Email can't be blank"

    if params[:email].blank? errors << "Password can't be blank" if params[:password].blank? errors << "Password is too short (minimum is 5 characters)" if params[:password].present? && params[:password].length < 5
  44. app/controllers/sessions_controller.rb flash.now[:error] = errors.any? ? errors.join(" ") : "Couldn't log

    you in as '#{params[:email]}'" logger.warn "Failed signin for '#{params[:email]}' at #{Time.now.utc}"
  45. app/controllers/sessions_controller.rb @email = params[:email] @remember_me = !!params[:remember_me] render :new

  46. CODE SMELLS

  47. MODEL VIEW CONTROLLER VIEWS AND CONTROLLERS WITHOUT A CLEAR SOURCE

    OF KNOWLEDGE X
  48. SCATTERED KNOWLEDGE KNOWLEDGE IN CONTROLLERS AND VIEWS

  49. Refactor with Caution Refactor with Tests

  50. features/login.feature Feature: Logging in As an anonymous user with an

    account I want to log in to my account So that I can be myself Background: Given the following user exists: | first_name | last_name | email | password | | President | Skroob | skroob@spaceball1.com | 12345 | And I am on the login page
  51. features/login.feature Scenario: Login successfully When I fill in "Email" with

    "skroob@spaceball1.com" And I fill in "Password" with "12345" And the "Remember me" checkbox should be checked And I press "Log in" Then I should see "Logged in successfully"
  52. $ cucumber features/login.feature Using the default profile... ......... 1 scenario

    (1 passed) 7 steps (7 passed) 0m0.971s
  53. features/login.feature Scenario: Submit blank form When I uncheck "Remember me"

    And I press "Log in" Then I should see "Email can't be blank" And I should see "Password can't be blank" And I should not see "Couldn't log you in" And I should not see "Password is too short" And the "Remember me" checkbox should not be checked
  54. $ cucumber features/login.feature Using the default profile... .................. 2 scenarios

    (2 passed) 16 steps (16 passed) 0m1.157s
  55. features/login.feature Scenario: Password too short When I fill in "Email"

    with "skroob@spaceball1.com" And I fill in "Password" with "1234" And I press "Log in" Then I should see "Password is too short (minimum is 5 characters)" And I should not see "Couldn't log you in as 'skroob@spaceball1.com'" And the "Password" field should not contain "1234"
  56. $ cucumber features/login.feature Using the default profile... .......................... 3 scenarios

    (3 passed) 24 steps (24 passed) 0m1.409s
  57. features/login.feature Scenario: Incorrect password When I fill in "Email" with

    "skroob@spaceball1.com" And I fill in "Password" with "54321" And I press "Log in" Then I should see "Couldn't log you in as 'skroob@spaceball1.com'" And the "Email" field should contain "skroob@spaceball1.com" And the "Password" field should not contain "54321"
  58. $ cucumber features/login.feature Using the default profile... .................................. 4 scenarios

    (4 passed) 32 steps (32 passed) 0m1.563s
  59. None
  60. A New View On Life <h1>Log In</h1> <%= form_for @login_request,

    :url => session_path do |f| %> <% @login_request.errors.full_messages.each do |message| %> <%= content_tag(:p, message) %> <% end %> <div class="field"> <%= f.label :email %><br /> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %><br /> <%= f.password_field :password %><br /> <%= link_to 'Forgot Password?', password_help_path %> </div> <div class="field"> <%= f.check_box :remember_me %> <%= f.label :remember_me %> </div> <div class="actions"> <%= f.submit "Log in" %> </div> <% end %>
  61. Before <% flash.each do |name, message| %> <%= content_tag(:p, message)

    %> <% end %> <h1>Log In</h1> <%= form_tag session_path do %> <%# … %> <% end %>
  62. After <h1>Log In</h1> <%= form_for @login_request, :url => session_path do

    |f| %> <% @login_request.errors.full_messages.each do |message| %> <%= content_tag(:p, message) %> <% end %> <%# … %> <% end %>
  63. Before <div class="field"> <%= label_tag 'email' %><br /> <%= text_field_tag

    'email', @email %> </div>
  64. After <div class="field"> <%= f.label :email %><br /> <%= f.text_field

    :email %> </div>
  65. Before <div class="field"> <%= label_tag 'password' %><br/> <%= password_field_tag 'password',

    nil %><br /> <%= link_to 'Forgot Password?', password_help_path %> </div>
  66. After <div class="field"> <%= f.label :password %><br /> <%= f.password_field

    :password %><br /> <%= link_to 'Forgot Password?', password_help_path %> </div>
  67. Before <div class="field"> <%= check_box_tag 'remember_me', '1', @remember_me.nil? ? true

    : @remember_me %> <%= label_tag 'remember_me' %> </div>
  68. After <div class="field"> <%= f.check_box :remember_me %> <%= f.label :remember_me

    %> </div>
  69. Before <div class="actions"> <%= submit_tag 'Log in' %> </div>

  70. After <div class="actions"> <%= f.submit "Log in" %> </div>

  71. Regaining Control class SessionsController < ApplicationController def new @login_request =

    LoginRequest.new end def create logout_keeping_session! @login_request = LoginRequest.new(params[:login_request]) if @login_request.authenticate self.current_user = @login_request.user handle_remember_cookie! @login_request.remember_me? flash[:notice] = "Logged in successfully." redirect_back_or_default root_url else render :new end end end
  72. Before def new end

  73. After def new @login_request = LoginRequest.new end

  74. Before & After def create logout_keeping_session!

  75. Before user = User.authenticate(params[:email], params[:password])

  76. After @login_request = LoginRequest.new(params[:login_request])

  77. Before if user self.current_user = user handle_remember_cookie! (params[:remember_me] == "1")

    flash[:notice] = "Logged in successfully." redirect_back_or_default root_url else
  78. After if @login_request.authenticate self.current_user = @login_request.user handle_remember_cookie! @login_request.remember_me? flash[:notice] =

    "Logged in successfully." redirect_back_or_default root_url else
  79. Before Part 1 errors = [] errors << "Email can't

    be blank" if params[:email].blank? errors << "Password can't be blank" if params[:password].blank? errors << "Password is too short (minimum is 5 characters)" if params[:password].present? && params[:password].length < 5
  80. Before Part 2 flash.now[:error] = errors.any? ? errors.join(" ") :

    "Couldn't log you in as '#{params[:email]}'" logger.warn "Failed signin for '#{params[:email]}' at #{Time.now.utc}"
  81. Before Part 3 @email = params[:email] @remember_me = !!params[:remember_me] render

    :new
  82. After render :new

  83. NOW WE LET THE TESTS TAKE THE WHEEL TEST DRIVEN

    DEVELOPMENT
  84. $ cucumber features/login.feature Using the default profile... .F.F------------------------------ (::) failed

    steps (::) uninitialized constant SessionsController::LoginRequest (NameError)
  85. app/models/login_request.rb class LoginRequest end

  86. $ cucumber features/login.feature Using the default profile... .F.F------------------------------ (::) failed

    steps (::) undefined method `model_name' for LoginRequest:Class (ActionView::Template::Error
  87. The ActiveModel API #to_model

  88. ActiveModel API #persisted? #valid? #errors #[] #full_messages #to_key #to_param #to_partial_path

    .model_name #human #singular #plural
  89. When does a model need to be an ActiveModel? Railties

    ActiveSupport ActiveRecord ActiveResource ActiveModel ActionMailer
  90. ActiveModel API is for ActionPack

  91. When does ActionPack need an ActiveModel? Polymorphic Routes (url_for) Partial

    Rendering (render) Record Identification (dom_class, dom_id) Form Building (form_for)
  92. ActiveModel::Lint::Tests

  93. $ rspec spec/models/ login_request_spec.rb FFFFFFF Failures: 1) LoginRequest it should

    behave like ActiveModel test model naming Failure/Error: send test Test::Unit::AssertionFailedError: The object should respond_to to_model. <false> is not true.
  94. app/models/login_request.rb class LoginRequest def to_model self end end

  95. $ rspec spec/models/ login_request_spec.rb FFFFFFF Failures: 1) LoginRequest it should

    behave like ActiveModel test model naming Failure/Error: send test Test::Unit::AssertionFailedError: The object should respond_to model_name. <false> is not true.
  96. The ActiveModel Library gem install activemodel

  97. ActiveModel::Naming .model_name #human #singular #plural

  98. app/models/login_request.rb class LoginRequest extend ActiveModel::Naming def to_model self end end

  99. $ rspec spec/models/ login_request_spec.rb .FFFFFF Failures: 1) LoginRequest it should

    behave like ActiveModel test to param Failure/Error: send test Test::Unit::AssertionFailedError: to_param should return nil when `persisted?` returns false. <false> is not true.
  100. ActiveModel::Conversion #to_model #to_key #to_param

  101. app/models/login_request.rb class LoginRequest extend ActiveModel::Naming include ActiveModel::Conversion end

  102. $ rspec spec/models/ login_request_spec.rb ...FFFF Failures: 1) LoginRequest it should

    behave like ActiveModel test valid? Failure/Error: send test Test::Unit::AssertionFailedError: The model should respond to valid?. <false> is not true.
  103. ActiveModel::Validations #valid? #errors .validates_acceptance_of .validates_confirmation_of .validates_exclusion_of .validates_format_of .validates_inclusion_of .validates_length_of .validates_numericality_of

    .validates_presence_of
  104. app/models/login_request.rb class LoginRequest extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations end

  105. $ rspec spec/models/ login_request_spec.rb ......F Failures: 1) LoginRequest it should

    behave like ActiveModel test persisted? Failure/Error: send test Test::Unit::AssertionFailedError: The model should respond to persisted?. <false> is not true.
  106. app/models/login_request.rb class LoginRequest extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations def

    persisted? false end end
  107. $ rspec spec/models/ login_request_spec.rb ....... Finished in 0.00835 seconds 7

    examples, 0 failures
  108. $ cucumber features/login.feature Using the default profile... .F.F------------------------------ (::) failed

    steps (::) undefined method `email' for #<LoginRequest:0x104c55fd0> (ActionView::Template::Error)
  109. app/models/login_request.rb attr_accessor :email, :password, :remember_me

  110. $ cucumber features/login.feature Using the default profile... ......F--...F-----....F---....F--- (::) failed

    steps (::) expected false to be true features/login.feature:15:in `And the "Remember me" checkbox should be checked'
  111. app/models/login_request.rb def initialize self.remember_me = true end

  112. $ cucumber features/login.feature Using the default profile... .......F-...F-----....F---....F--- (::) failed

    steps (::) wrong number of arguments (1 for 0) (ArgumentError) ./app/controllers/ sessions_controller.rb:10:in `initialize'
  113. app/controllers/sessions_controller.rb @login_request = LoginRequest.new(params[:login_request])

  114. app/models/login_request.rb def initialize(new_attributes={}) self.remember_me = true new_attributes.each do |attribute, value|

    if respond_to?("#{attribute}=") send("#{attribute}=", value) end end end
  115. $ cucumber features/login.feature Using the default profile... .......F-...F-----....F---....F--- (::) failed

    steps (::) undefined method `authenticate' for #<LoginRequest:0x1178886b0> (NoMethodError) ./app/controllers/ sessions_controller.rb:12:in `create'
  116. app/models/login_request.rb def authenticate success = valid? logger.warn "Failed signin for

    #{email.inspect}" unless success success end def logger Rails.logger end
  117. $ cucumber features/login.feature Using the default profile... .......F-...F-----....F---....F--- (::) failed

    steps (::) undefined method `user' for #<LoginRequest:0x107bd64f8> (NoMethodError) ./app/controllers/ sessions_controller.rb:13:in `create'
  118. app/models/login_request.rb def user return @user if instance_variable_defined? "@user" @user =

    User.authenticate(email, password) end
  119. $ cucumber features/login.feature Using the default profile... .......F-...F-----....F---....F--- (::) failed

    steps (::) undefined method `remember_me?' for #<LoginRequest:0x111efadf0> (NoMethodError) ./app/controllers/ sessions_controller.rb:14:in `create'
  120. app/models/login_request.rb def remember_me? remember_me.present? end

  121. $ cucumber features/login.feature Using the default profile... .............F----.....F--.....F-- (::) failed

    steps (::) expected there to be content "Email can't be blank"
  122. app/models/login_request.rb validates_presence_of :email, :password validates_length_of :password, :minimum => 5, :allow_blank

    => true
  123. $ cucumber features/login.feature Using the default profile... ...............................F-- (::) failed

    steps (::) expected there to be content "Couldn't log you in as 'skroob@spaceball1.com'"
  124. app/models/login_request.rb validate do if errors.empty? && user.blank? errors.add :base, "Couldn't

    log you in as '#{email}'" end end
  125. $ cucumber features/login.feature Using the default profile... .................................. 4 scenarios

    (4 passed) 32 steps (32 passed) 0m1.599s
  126. class LoginRequest extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations def persisted?

    false end attr_accessor :email, :password, :remember_me def initialize(new_attributes={}) self.remember_me = true new_attributes.each do |attribute, value| if respond_to?("#{attribute}=") send("#{attribute}=", value) end end end def authenticate success = valid? logger.warn "Failed signin for #{email.inspect}" unless success success end def logger Rails.logger end def user return @user if instance_variable_defined? "@user" @user = User.authenticate(email, password) end def remember_me? remember_me.present? end validates_presence_of :email, :password validates_length_of :password, :minimum => 5, :allow_blank => true validate do if errors.empty? && user.blank? errors.add :base, "Couldn't log you in as '#{email}'" end end end
  127. ActiveAttr What ActiveModel Left Out

  128. Rocket Fuel For your models

  129. BasicModel class Person include ActiveAttr::BasicModel end Person.model_name.plural #=> "people" person

    = Person.new person.valid? #=> true person.errors.full_messages #=> []
  130. Before extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations def persisted? false

    end
  131. After include ActiveAttr::BasicModel

  132. Attributes class Person include ActiveAttr::Attributes attribute :first_name attribute :last_name end

    person = Person.new person.first_name = "Chris" person.last_name = "Griego" person.attributes #=> {"first_name"=>"Chris", "last_name"=>"Griego"}
  133. Before attr_accessor :email, :password, :remember_me

  134. After include ActiveAttr::Attributes attribute :email attribute :password attribute :remember_me

  135. QueryAttributes class Person include ActiveAttr::QueryAttributes attribute :first_name attribute :last_name end

    person = Person.new person.first_name = "Chris" person.first_name? #=> true person.last_name? #=> false
  136. Before def remember_me? remember_me.present? end

  137. After include ActiveAttr::QueryAttributes

  138. AttributeDefaults class Person include ActiveAttr::AttributeDefaults attribute :first_name, :default => "John"

    attribute :last_name, :default => "Doe" end person = Person.new person.first_name #=> "John" person.last_name #=> "Doe"
  139. Before attribute :remember_me def initialize(new_attributes={}) self.remember_me = true new_attributes.each do

    |attribute, value| if respond_to?("#{attribute}=") send("#{attribute}=", value) end end end
  140. After include ActiveAttr::AttributeDefaults attribute :remember_me, :default => true def initialize(new_attributes={})

    new_attributes.each do |attribute, value| if respond_to?("#{attribute}=") send("#{attribute}=", value) end end end
  141. MassAssignment class Person include ActiveAttr::MassAssignment attr_accessor :first_name, :last_name end person

    = Person.new(:first_name => "Chris") person.attributes = { :last_name => "Griego" } person.first_name #=> "Chris" person.last_name #=> "Griego"
  142. Before def initialize(new_attributes={}) new_attributes.each do |attribute, value| if respond_to?("#{attribute}=") send("#{attribute}=",

    value) end end end
  143. After include ActiveAttr::MassAssignment

  144. Logger class Person include ActiveAttr::Logger end Person.logger = Logger.new(STDOUT) Person.logger?

    #=> true Person.logger.info "Logging an informational message" person = Person.new person.logger? #=> true person.logger = Logger.new(STDERR) person.logger.warn "Logging a warning message"
  145. Before def logger Rails.logger end

  146. After include ActiveAttr::Logger

  147. Model class Person include ActiveAttr::Model end

  148. Before include ActiveAttr::BasicModel include ActiveAttr::Attributes include ActiveAttr::QueryAttributes include ActiveAttr::AttributeDefaults include

    ActiveAttr::MassAssignment include ActiveAttr::Logger
  149. After include ActiveAttr::Model

  150. class LoginRequest include ActiveAttr::Model attribute :email attribute :password attribute :remember_me,

    :default => true def authenticate success = valid? logger.warn "Failed signin for #{email.inspect}" unless success success end def user return @user if instance_variable_defined? "@user" @user = User.authenticate(email, password) end validates_presence_of :email, :password validates_length_of :password, :minimum => 5, :allow_blank => true validate do if errors.empty? && user.blank? errors.add :base, "Couldn't log you in as '#{email}'" end end end
  151. $ cucumber features/login.feature Using the default profile... .................................. 4 scenarios

    (4 passed) 32 steps (32 passed) 0m1.731s
  152. $ rspec spec/models/ login_request_spec.rb .................................. Finished in 0.09344 seconds 34

    examples, 0 failures
  153. MassAssignmentSecurity class Person include ActiveAttr::MassAssignmentSecurity attr_accessor :first_name, :last_name attr_protected :last_name

    end person = Person.new(:first_name => "Chris", :last_name => "Griego") person.first_name #=> "Chris" person.last_name #=> nil
  154. BlockInitialization class Person include ActiveAttr::BlockInitialization attr_accessor :first_name, :last_name end person

    = Person.new do |p| p.first_name = "Chris" p.last_name = "Griego" end person.first_name #=> "Chris" person.last_name #=> "Griego"
  155. TypecastedAttributes class Person include ActiveAttr::TypecastedAttributes attribute :age, :type => Integer

    end person = Person.new person.age = "29" person.age #=> 29
  156. Rails Integration gem "active_attr"

  157. RSpec Integration require "active_attr/rspec" describe Person do it { should

    have_attribute(:first_name).with_default_value_of("John") } end
  158. ActiveAttr Roadmap Multi-parameter Attributes Non-nullable Attributes Rails Generators Associations Nested

    Attributes
  159. Image Credits The Elephant in the Room by Bit Boy

    http://www.flickr.com/photos/bitboy/246805948/ Binoculars by Evan Long http://www.flickr.com/photos/clover_1/1200447508/ Hiding by Susan Sermoneta http://www.flickr.com/photos/en321/33868864/ PS3 Controller by Mauro Monti http://www.flickr.com/photos/kilamdil/393406895/ Xbox Controller by Alexa Booth http://www.flickr.com/photos/xabooth/2208511222/ Nintendo Gamecube Controller by Andy Zeigert http://www.flickr.com/photos/andyz/40676384/ View and Rooftops by C.J. Peters http://www.flickr.com/photos/conlawprof/266890004/ Glittering City View by William Cho http://www.flickr.com/photos/adforce1/3876421475/ Korean Signs by Taylor Sloan http://www.flickr.com/photos/taylorsloan/4477937283/ Flint Knapper by Wessex Archaeology http://www.flickr.com/photos/wessexarchaeology/56515302/ Hat Collection by lokarta http://www.flickr.com/photos/lokar/4071667927/ Caution Men by Alyson Hurt http://www.flickr.com/photos/alykat/2930096885/ Smells by Larissa http://www.flickr.com/photos/lara68/3492153226/ Wheel Building by Andrew Schwab http://www.flickr.com/photos/aschwab/3621428436/ Socony Gas Can by BEV Norton http://www.flickr.com/photos/catchesthelight/2225634867/ Statue of the Ancient Mariner Photo by pshab http://www.flickr.com/photos/pshab/460051997/
  160. He went like one that hath been stunned, And is

    of sense forlorn: A sadder and a wiser man, He rose the morrow morn.