shoulda 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +12 -0
  2. data/MIT-LICENSE +22 -0
  3. data/README.rdoc +132 -0
  4. data/Rakefile +72 -0
  5. data/bin/convert_to_should_syntax +42 -0
  6. data/lib/shoulda.rb +16 -0
  7. data/lib/shoulda/action_mailer.rb +10 -0
  8. data/lib/shoulda/action_mailer/assertions.rb +39 -0
  9. data/lib/shoulda/active_record.rb +12 -0
  10. data/lib/shoulda/active_record/assertions.rb +85 -0
  11. data/lib/shoulda/active_record/macros.rb +703 -0
  12. data/lib/shoulda/assertions.rb +45 -0
  13. data/lib/shoulda/context.rb +309 -0
  14. data/lib/shoulda/controller.rb +30 -0
  15. data/lib/shoulda/controller/formats/html.rb +201 -0
  16. data/lib/shoulda/controller/formats/xml.rb +170 -0
  17. data/lib/shoulda/controller/helpers.rb +64 -0
  18. data/lib/shoulda/controller/macros.rb +287 -0
  19. data/lib/shoulda/controller/resource_options.rb +236 -0
  20. data/lib/shoulda/controller/routing.rb +0 -0
  21. data/lib/shoulda/controller/routing/macros.rb +0 -0
  22. data/lib/shoulda/helpers.rb +10 -0
  23. data/lib/shoulda/macros.rb +80 -0
  24. data/lib/shoulda/private_helpers.rb +22 -0
  25. data/lib/shoulda/proc_extensions.rb +14 -0
  26. data/lib/shoulda/rails.rb +19 -0
  27. data/lib/shoulda/tasks.rb +3 -0
  28. data/lib/shoulda/tasks/list_tests.rake +24 -0
  29. data/lib/shoulda/tasks/yaml_to_shoulda.rake +28 -0
  30. data/test/README +36 -0
  31. data/test/fixtures/addresses.yml +3 -0
  32. data/test/fixtures/friendships.yml +0 -0
  33. data/test/fixtures/posts.yml +5 -0
  34. data/test/fixtures/products.yml +0 -0
  35. data/test/fixtures/taggings.yml +0 -0
  36. data/test/fixtures/tags.yml +9 -0
  37. data/test/fixtures/users.yml +6 -0
  38. data/test/functional/posts_controller_test.rb +104 -0
  39. data/test/functional/users_controller_test.rb +36 -0
  40. data/test/other/context_test.rb +145 -0
  41. data/test/other/convert_to_should_syntax_test.rb +63 -0
  42. data/test/other/helpers_test.rb +183 -0
  43. data/test/other/private_helpers_test.rb +34 -0
  44. data/test/other/should_test.rb +266 -0
  45. data/test/rails_root/app/controllers/application.rb +25 -0
  46. data/test/rails_root/app/controllers/posts_controller.rb +85 -0
  47. data/test/rails_root/app/controllers/users_controller.rb +81 -0
  48. data/test/rails_root/app/helpers/application_helper.rb +3 -0
  49. data/test/rails_root/app/helpers/posts_helper.rb +2 -0
  50. data/test/rails_root/app/helpers/users_helper.rb +2 -0
  51. data/test/rails_root/app/models/address.rb +7 -0
  52. data/test/rails_root/app/models/dog.rb +5 -0
  53. data/test/rails_root/app/models/flea.rb +3 -0
  54. data/test/rails_root/app/models/friendship.rb +4 -0
  55. data/test/rails_root/app/models/post.rb +12 -0
  56. data/test/rails_root/app/models/product.rb +12 -0
  57. data/test/rails_root/app/models/tag.rb +8 -0
  58. data/test/rails_root/app/models/tagging.rb +4 -0
  59. data/test/rails_root/app/models/user.rb +28 -0
  60. data/test/rails_root/app/views/layouts/posts.rhtml +17 -0
  61. data/test/rails_root/app/views/layouts/users.rhtml +17 -0
  62. data/test/rails_root/app/views/posts/edit.rhtml +27 -0
  63. data/test/rails_root/app/views/posts/index.rhtml +25 -0
  64. data/test/rails_root/app/views/posts/new.rhtml +26 -0
  65. data/test/rails_root/app/views/posts/show.rhtml +18 -0
  66. data/test/rails_root/app/views/users/edit.rhtml +22 -0
  67. data/test/rails_root/app/views/users/index.rhtml +22 -0
  68. data/test/rails_root/app/views/users/new.rhtml +21 -0
  69. data/test/rails_root/app/views/users/show.rhtml +13 -0
  70. data/test/rails_root/config/boot.rb +109 -0
  71. data/test/rails_root/config/database.yml +4 -0
  72. data/test/rails_root/config/environment.rb +14 -0
  73. data/test/rails_root/config/environments/sqlite3.rb +0 -0
  74. data/test/rails_root/config/initializers/new_rails_defaults.rb +15 -0
  75. data/test/rails_root/config/initializers/shoulda.rb +8 -0
  76. data/test/rails_root/config/routes.rb +6 -0
  77. data/test/rails_root/db/migrate/001_create_users.rb +18 -0
  78. data/test/rails_root/db/migrate/002_create_posts.rb +13 -0
  79. data/test/rails_root/db/migrate/003_create_taggings.rb +12 -0
  80. data/test/rails_root/db/migrate/004_create_tags.rb +11 -0
  81. data/test/rails_root/db/migrate/005_create_dogs.rb +12 -0
  82. data/test/rails_root/db/migrate/006_create_addresses.rb +14 -0
  83. data/test/rails_root/db/migrate/007_create_fleas.rb +11 -0
  84. data/test/rails_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  85. data/test/rails_root/db/migrate/009_create_products.rb +17 -0
  86. data/test/rails_root/db/migrate/010_create_friendships.rb +14 -0
  87. data/test/rails_root/db/schema.rb +0 -0
  88. data/test/rails_root/log/sqlite3.log +0 -0
  89. data/test/rails_root/public/404.html +30 -0
  90. data/test/rails_root/public/422.html +30 -0
  91. data/test/rails_root/public/500.html +30 -0
  92. data/test/rails_root/script/console +3 -0
  93. data/test/rails_root/script/generate +3 -0
  94. data/test/test_helper.rb +33 -0
  95. data/test/unit/address_test.rb +10 -0
  96. data/test/unit/dog_test.rb +7 -0
  97. data/test/unit/flea_test.rb +6 -0
  98. data/test/unit/friendship_test.rb +6 -0
  99. data/test/unit/post_test.rb +15 -0
  100. data/test/unit/product_test.rb +27 -0
  101. data/test/unit/tag_test.rb +14 -0
  102. data/test/unit/tagging_test.rb +6 -0
  103. data/test/unit/user_test.rb +52 -0
  104. metadata +170 -0
@@ -0,0 +1,236 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Controller
4
+ # Formats tested by #should_be_restful. Defaults to [:html, :xml]
5
+ VALID_FORMATS = Dir.glob(File.join(File.dirname(__FILE__), 'formats', '*.rb')).map { |f| File.basename(f, '.rb') }.map(&:to_sym) # :doc:
6
+ VALID_FORMATS.each {|f| require "shoulda/controller/formats/#{f}"}
7
+
8
+ # Actions tested by #should_be_restful
9
+ VALID_ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy] # :doc:
10
+
11
+ # A ResourceOptions object is passed into should_be_restful in order to configure the tests for your controller.
12
+ #
13
+ # Example:
14
+ # class UsersControllerTest < Test::Unit::TestCase
15
+ # fixtures :all
16
+ #
17
+ # def setup
18
+ # ...normal setup code...
19
+ # @user = User.find(:first)
20
+ # end
21
+ #
22
+ # should_be_restful do |resource|
23
+ # resource.identifier = :id
24
+ # resource.klass = User
25
+ # resource.object = :user
26
+ # resource.parent = []
27
+ # resource.actions = [:index, :show, :new, :edit, :update, :create, :destroy]
28
+ # resource.formats = [:html, :xml]
29
+ #
30
+ # resource.create.params = { :name => "bob", :email => 'bob@bob.com', :age => 13}
31
+ # resource.update.params = { :name => "sue" }
32
+ #
33
+ # resource.create.redirect = "user_url(@user)"
34
+ # resource.update.redirect = "user_url(@user)"
35
+ # resource.destroy.redirect = "users_url"
36
+ #
37
+ # resource.create.flash = /created/i
38
+ # resource.update.flash = /updated/i
39
+ # resource.destroy.flash = /removed/i
40
+ # end
41
+ # end
42
+ #
43
+ # Whenever possible, the resource attributes will be set to sensible defaults.
44
+ #
45
+ class ResourceOptions
46
+ # Configuration options for the create, update, destroy actions under should_be_restful
47
+ class ActionOptions
48
+ # String evaled to get the target of the redirection.
49
+ # All of the instance variables set by the controller will be available to the
50
+ # evaled code.
51
+ #
52
+ # Example:
53
+ # resource.create.redirect = "user_url(@user.company, @user)"
54
+ #
55
+ # Defaults to a generated url based on the name of the controller, the action, and the resource.parents list.
56
+ attr_accessor :redirect
57
+
58
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
59
+ #
60
+ # Defaults:
61
+ # destroy:: /removed/
62
+ # create:: /created/
63
+ # update:: /updated/
64
+ attr_accessor :flash
65
+
66
+ # Hash describing the params that should be sent in with this action.
67
+ attr_accessor :params
68
+ end
69
+
70
+ # Configuration options for the denied actions under should_be_restful
71
+ #
72
+ # Example:
73
+ # context "The public" do
74
+ # setup do
75
+ # @request.session[:logged_in] = false
76
+ # end
77
+ #
78
+ # should_be_restful do |resource|
79
+ # resource.parent = :user
80
+ #
81
+ # resource.denied.actions = [:index, :show, :edit, :new, :create, :update, :destroy]
82
+ # resource.denied.flash = /get outta here/i
83
+ # resource.denied.redirect = 'new_session_url'
84
+ # end
85
+ # end
86
+ #
87
+ class DeniedOptions
88
+ # String evaled to get the target of the redirection.
89
+ # All of the instance variables set by the controller will be available to the
90
+ # evaled code.
91
+ #
92
+ # Example:
93
+ # resource.create.redirect = "user_url(@user.company, @user)"
94
+ attr_accessor :redirect
95
+
96
+ # String or Regexp describing a value expected in the flash. Will match against any flash key.
97
+ #
98
+ # Example:
99
+ # resource.create.flash = /created/
100
+ attr_accessor :flash
101
+
102
+ # Actions that should be denied (only used by resource.denied). <i>Note that these actions will
103
+ # only be tested if they are also listed in +resource.actions+</i>
104
+ # The special value of :all will deny all of the REST actions.
105
+ attr_accessor :actions
106
+ end
107
+
108
+ # Name of key in params that references the primary key.
109
+ # Will almost always be :id (default), unless you are using a plugin or have patched rails.
110
+ attr_accessor :identifier
111
+
112
+ # Name of the ActiveRecord class this resource is responsible for. Automatically determined from
113
+ # test class if not explicitly set. UserTest => "User"
114
+ attr_accessor :klass
115
+
116
+ # Name of the instantiated ActiveRecord object that should be used by some of the tests.
117
+ # Defaults to the underscored name of the AR class. CompanyManager => :company_manager
118
+ attr_accessor :object
119
+
120
+ # Name of the parent AR objects. Can be set as parent= or parents=, and can take either
121
+ # the name of the parent resource (if there's only one), or an array of names (if there's
122
+ # more than one).
123
+ #
124
+ # Example:
125
+ # # in the routes...
126
+ # map.resources :companies do
127
+ # map.resources :people do
128
+ # map.resources :limbs
129
+ # end
130
+ # end
131
+ #
132
+ # # in the tests...
133
+ # class PeopleControllerTest < Test::Unit::TestCase
134
+ # should_be_restful do |resource|
135
+ # resource.parent = :companies
136
+ # end
137
+ # end
138
+ #
139
+ # class LimbsControllerTest < Test::Unit::TestCase
140
+ # should_be_restful do |resource|
141
+ # resource.parents = [:companies, :people]
142
+ # end
143
+ # end
144
+ attr_accessor :parent
145
+ alias parents parent
146
+ alias parents= parent=
147
+
148
+ # Actions that should be tested. Must be a subset of VALID_ACTIONS (default).
149
+ # Tests for each actionw will only be generated if the action is listed here.
150
+ # The special value of :all will test all of the REST actions.
151
+ #
152
+ # Example (for a read-only controller):
153
+ # resource.actions = [:show, :index]
154
+ attr_accessor :actions
155
+
156
+ # Formats that should be tested. Must be a subset of VALID_FORMATS (default).
157
+ # Each action will be tested against the formats listed here. The special value
158
+ # of :all will test all of the supported formats.
159
+ #
160
+ # Example:
161
+ # resource.actions = [:html, :xml]
162
+ attr_accessor :formats
163
+
164
+ # ActionOptions object specifying options for the create action.
165
+ attr_accessor :create
166
+
167
+ # ActionOptions object specifying options for the update action.
168
+ attr_accessor :update
169
+
170
+ # ActionOptions object specifying options for the desrtoy action.
171
+ attr_accessor :destroy
172
+
173
+ # DeniedOptions object specifying which actions should return deny a request, and what should happen in that case.
174
+ attr_accessor :denied
175
+
176
+ def initialize # :nodoc:
177
+ @create = ActionOptions.new
178
+ @update = ActionOptions.new
179
+ @destroy = ActionOptions.new
180
+ @denied = DeniedOptions.new
181
+
182
+ @create.flash ||= /created/i
183
+ @update.flash ||= /updated/i
184
+ @destroy.flash ||= /removed/i
185
+ @denied.flash ||= /denied/i
186
+
187
+ @create.params ||= {}
188
+ @update.params ||= {}
189
+
190
+ @actions = VALID_ACTIONS
191
+ @formats = VALID_FORMATS
192
+ @denied.actions = []
193
+ end
194
+
195
+ def normalize!(target) # :nodoc:
196
+ @denied.actions = VALID_ACTIONS if @denied.actions == :all
197
+ @actions = VALID_ACTIONS if @actions == :all
198
+ @formats = VALID_FORMATS if @formats == :all
199
+
200
+ @denied.actions = @denied.actions.map(&:to_sym)
201
+ @actions = @actions.map(&:to_sym)
202
+ @formats = @formats.map(&:to_sym)
203
+
204
+ ensure_valid_members(@actions, VALID_ACTIONS, 'actions')
205
+ ensure_valid_members(@denied.actions, VALID_ACTIONS, 'denied.actions')
206
+ ensure_valid_members(@formats, VALID_FORMATS, 'formats')
207
+
208
+ @identifier ||= :id
209
+ @klass ||= target.name.gsub(/ControllerTest$/, '').singularize.constantize
210
+ @object ||= @klass.name.tableize.singularize
211
+ @parent ||= []
212
+ @parent = [@parent] unless @parent.is_a? Array
213
+
214
+ collection_helper = [@parent, @object.to_s.pluralize, 'url'].flatten.join('_')
215
+ collection_args = @parent.map {|n| "@#{object}.#{n}"}.join(', ')
216
+ @destroy.redirect ||= "#{collection_helper}(#{collection_args})"
217
+
218
+ member_helper = [@parent, @object, 'url'].flatten.join('_')
219
+ member_args = [@parent.map {|n| "@#{object}.#{n}"}, "@#{object}"].flatten.join(', ')
220
+ @create.redirect ||= "#{member_helper}(#{member_args})"
221
+ @update.redirect ||= "#{member_helper}(#{member_args})"
222
+ @denied.redirect ||= "new_session_url"
223
+ end
224
+
225
+ private
226
+
227
+ def ensure_valid_members(ary, valid_members, name) # :nodoc:
228
+ invalid = ary - valid_members
229
+ raise ArgumentError, "Unsupported #{name}: #{invalid.inspect}" unless invalid.empty?
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+
File without changes
@@ -0,0 +1,10 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Helpers
4
+ # Prints a message to stdout, tagged with the name of the calling method.
5
+ def report!(msg = "")
6
+ puts("#{caller.first}: #{msg}")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,80 @@
1
+ require 'shoulda/private_helpers'
2
+
3
+ module ThoughtBot # :nodoc:
4
+ module Shoulda # :nodoc:
5
+ module Macros
6
+ # Macro that creates a test asserting a change between the return value
7
+ # of an expression that is run before and after the current setup block
8
+ # is run. This is similar to Active Support's <tt>assert_difference</tt>
9
+ # assertion, but supports more than just numeric values. See also
10
+ # should_not_change.
11
+ #
12
+ # Example:
13
+ #
14
+ # context "Creating a post"
15
+ # setup do
16
+ # Post.create
17
+ # end
18
+ #
19
+ # should_change "Post.count", :by => 1
20
+ # end
21
+ #
22
+ # As shown in this example, the <tt>:by</tt> option expects a numeric
23
+ # difference between the before and after values of the expression. You
24
+ # may also specify <tt>:from</tt> and <tt>:to</tt> options:
25
+ #
26
+ # should_change "Post.count", :from => 0, :to => 1
27
+ # should_change "@post.title", :from => "old", :to => "new"
28
+ #
29
+ # Combinations of <tt>:by</tt>, <tt>:from</tt>, and <tt>:to</tt> are allowed:
30
+ #
31
+ # should_change "@post.title" # => assert the value changed in some way
32
+ # should_change "@post.title" :from => "old" # => assert the value changed to anything other than "old"
33
+ # should_change "@post.title" :to => "new" # => assert the value changed from anything other than "new"
34
+ def should_change(expression, options = {})
35
+ by, from, to = get_options!([options], :by, :from, :to)
36
+ stmt = "change #{expression.inspect}"
37
+ stmt << " from #{from.inspect}" if from
38
+ stmt << " to #{to.inspect}" if to
39
+ stmt << " by #{by.inspect}" if by
40
+
41
+ expression_eval = lambda { eval(expression) }
42
+ before = lambda { @_before_should_change = expression_eval.bind(self).call }
43
+ should stmt, :before => before do
44
+ old_value = @_before_should_change
45
+ new_value = expression_eval.bind(self).call
46
+ assert_operator from, :===, old_value, "#{expression.inspect} did not originally match #{from.inspect}" if from
47
+ assert_not_equal old_value, new_value, "#{expression.inspect} did not change" unless by == 0
48
+ assert_operator to, :===, new_value, "#{expression.inspect} was not changed to match #{to.inspect}" if to
49
+ assert_equal old_value + by, new_value if by
50
+ end
51
+ end
52
+
53
+ # Macro that creates a test asserting no change between the return value
54
+ # of an expression that is run before and after the current setup block
55
+ # is run. This is the logical opposite of should_change.
56
+ #
57
+ # Example:
58
+ #
59
+ # context "Updating a post"
60
+ # setup do
61
+ # @post.update_attributes(:title => "new")
62
+ # end
63
+ #
64
+ # should_not_change "Post.count"
65
+ # end
66
+ def should_not_change(expression)
67
+ expression_eval = lambda { eval(expression) }
68
+ before = lambda { @_before_should_not_change = expression_eval.bind(self).call }
69
+ should "not change #{expression.inspect}", :before => before do
70
+ new_value = expression_eval.bind(self).call
71
+ assert_equal @_before_should_not_change, new_value, "#{expression.inspect} changed"
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ include ThoughtBot::Shoulda::Private
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,22 @@
1
+ module ThoughtBot # :nodoc:
2
+ module Shoulda # :nodoc:
3
+ module Private # :nodoc:
4
+ # Returns the values for the entries in the args hash who's keys are listed in the wanted array.
5
+ # Will raise if there are keys in the args hash that aren't listed.
6
+ def get_options!(args, *wanted)
7
+ ret = []
8
+ opts = (args.last.is_a?(Hash) ? args.pop : {})
9
+ wanted.each {|w| ret << opts.delete(w)}
10
+ raise ArgumentError, "Unsupported options given: #{opts.keys.join(', ')}" unless opts.keys.empty?
11
+ return *ret
12
+ end
13
+
14
+ # Returns the model class constant, as determined by the test class name.
15
+ #
16
+ # class TestUser; model_class; end => User
17
+ def model_class
18
+ self.name.gsub(/Test$/, '').constantize
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ # Stolen straight from ActiveSupport
2
+
3
+ class Proc #:nodoc:
4
+ def bind(object)
5
+ block, time = self, Time.now
6
+ (class << object; self end).class_eval do
7
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
8
+ define_method(method_name, &block)
9
+ method = instance_method(method_name)
10
+ remove_method(method_name)
11
+ method
12
+ end.bind(object)
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'shoulda'
4
+
5
+ require 'shoulda/active_record' if defined? ActiveRecord::Base
6
+ require 'shoulda/controller' if defined? ActionController::Base
7
+ require 'shoulda/action_mailer' if defined? ActionMailer::Base
8
+
9
+ if defined?(RAILS_ROOT)
10
+ # load in the 3rd party macros from vendorized plugins and gems
11
+ Dir[File.join(RAILS_ROOT, "vendor", "{plugins,gems}", "*", "shoulda_macros", "*.rb")].each do |macro_file_path|
12
+ require macro_file_path
13
+ end
14
+
15
+ # load in the local application specific macros
16
+ Dir[File.join(RAILS_ROOT, "test", "shoulda_macros", "*.rb")].each do |macro_file_path|
17
+ require macro_file_path
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'tasks', '*.rake')].each do |f|
2
+ load f
3
+ end
@@ -0,0 +1,24 @@
1
+ namespace :shoulda do
2
+ desc "List the names of the test methods in a specification like format"
3
+ task :list do
4
+ $LOAD_PATH.unshift("test")
5
+
6
+ require 'test/unit'
7
+ require 'rubygems'
8
+ require 'active_support'
9
+
10
+ # bug in test unit. Set to true to stop from running.
11
+ Test::Unit.run = true
12
+
13
+ test_files = Dir.glob(File.join('test', '**', '*_test.rb'))
14
+ test_files.each do |file|
15
+ load file
16
+ klass = File.basename(file, '.rb').classify.constantize
17
+
18
+ puts klass.name.gsub('Test', '')
19
+
20
+ test_methods = klass.instance_methods.grep(/^test/).map {|s| s.gsub(/^test: /, '')}.sort
21
+ test_methods.each {|m| puts " " + m }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ namespace :shoulda do
2
+ # From http://blog.internautdesign.com/2007/11/2/a-yaml_to_shoulda-rake-task
3
+ # David.Lowenfels@gmail.com
4
+ desc "Converts a YAML file (FILE=./path/to/yaml) into a Shoulda skeleton"
5
+ task :from_yaml do
6
+ require 'yaml'
7
+
8
+ def yaml_to_context(hash, indent = 0)
9
+ indent1 = ' ' * indent
10
+ indent2 = ' ' * (indent + 1)
11
+ hash.each_pair do |context, shoulds|
12
+ puts indent1 + "context \"#{context}\" do"
13
+ puts
14
+ shoulds.each do |should|
15
+ yaml_to_context( should, indent + 1 ) and next if should.is_a?( Hash )
16
+ puts indent2 + "should_eventually \"" + should.gsub(/^should +/,'') + "\" do"
17
+ puts indent2 + "end"
18
+ puts
19
+ end
20
+ puts indent1 + "end"
21
+ end
22
+ end
23
+
24
+ puts("Please pass in a FILE argument.") and exit unless ENV['FILE']
25
+
26
+ yaml_to_context( YAML.load_file( ENV['FILE'] ) )
27
+ end
28
+ end