bret-watircraft 0.4.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 (83) hide show
  1. data/BUGS.txt +11 -0
  2. data/History.txt +209 -0
  3. data/Manifest.txt +103 -0
  4. data/README.rdoc +75 -0
  5. data/VERSION.yml +4 -0
  6. data/app_generators/watircraft/USAGE +11 -0
  7. data/app_generators/watircraft/templates/config.yml.erb +3 -0
  8. data/app_generators/watircraft/templates/feature_helper.rb +12 -0
  9. data/app_generators/watircraft/templates/initialize.rb.erb +10 -0
  10. data/app_generators/watircraft/templates/rakefile.rb +3 -0
  11. data/app_generators/watircraft/templates/script/console +5 -0
  12. data/app_generators/watircraft/templates/script/console.cmd +1 -0
  13. data/app_generators/watircraft/templates/site_start.rb.erb +12 -0
  14. data/app_generators/watircraft/templates/spec_helper.rb +9 -0
  15. data/app_generators/watircraft/templates/spec_initialize.rb +16 -0
  16. data/app_generators/watircraft/templates/world.rb +12 -0
  17. data/app_generators/watircraft/watircraft_generator.rb +108 -0
  18. data/bin/watircraft +17 -0
  19. data/lib/extensions/array.rb +10 -0
  20. data/lib/extensions/hash.rb +5 -0
  21. data/lib/extensions/object.rb +24 -0
  22. data/lib/extensions/string.rb +17 -0
  23. data/lib/extensions/watir.rb +41 -0
  24. data/lib/taza/browser.rb +45 -0
  25. data/lib/taza/entity.rb +34 -0
  26. data/lib/taza/fixture.rb +66 -0
  27. data/lib/taza/flow.rb +40 -0
  28. data/lib/taza/page.rb +259 -0
  29. data/lib/taza/settings.rb +80 -0
  30. data/lib/taza/site.rb +227 -0
  31. data/lib/taza/tasks.rb +30 -0
  32. data/lib/taza.rb +35 -0
  33. data/lib/watircraft/generator_helper.rb +27 -0
  34. data/lib/watircraft/table.rb +56 -0
  35. data/lib/watircraft/version.rb +3 -0
  36. data/lib/watircraft.rb +1 -0
  37. data/spec/array_spec.rb +16 -0
  38. data/spec/browser_spec.rb +68 -0
  39. data/spec/entity_spec.rb +9 -0
  40. data/spec/fake_table.rb +34 -0
  41. data/spec/fixture_spec.rb +34 -0
  42. data/spec/fixtures_spec.rb +21 -0
  43. data/spec/hash_spec.rb +12 -0
  44. data/spec/object_spec.rb +29 -0
  45. data/spec/page_generator_spec.rb +111 -0
  46. data/spec/page_spec.rb +342 -0
  47. data/spec/project_generator_spec.rb +103 -0
  48. data/spec/sandbox/config/config.yml +1 -0
  49. data/spec/sandbox/config/environments.yml +4 -0
  50. data/spec/sandbox/config/simpler.yml +1 -0
  51. data/spec/sandbox/config/simpler_site.yml +2 -0
  52. data/spec/sandbox/config.yml +2 -0
  53. data/spec/sandbox/fixtures/examples.yml +8 -0
  54. data/spec/sandbox/fixtures/users.yml +2 -0
  55. data/spec/sandbox/flows/batman.rb +5 -0
  56. data/spec/sandbox/flows/robin.rb +4 -0
  57. data/spec/sandbox/pages/foo/bar_page.rb +9 -0
  58. data/spec/sandbox/pages/foo/partials/partial_the_reckoning.rb +2 -0
  59. data/spec/settings_spec.rb +103 -0
  60. data/spec/site_generator_spec.rb +62 -0
  61. data/spec/site_spec.rb +249 -0
  62. data/spec/spec_generator_helper.rb +40 -0
  63. data/spec/spec_generator_spec.rb +24 -0
  64. data/spec/spec_helper.rb +21 -0
  65. data/spec/steps_generator_spec.rb +29 -0
  66. data/spec/string_spec.rb +17 -0
  67. data/spec/table_spec.rb +32 -0
  68. data/spec/taza_spec.rb +12 -0
  69. data/spec/watircraft_bin_spec.rb +14 -0
  70. data/watircraft.gemspec +53 -0
  71. data/watircraft_generators/page/USAGE +11 -0
  72. data/watircraft_generators/page/page_generator.rb +65 -0
  73. data/watircraft_generators/page/templates/page.rb.erb +8 -0
  74. data/watircraft_generators/site/site_generator.rb +51 -0
  75. data/watircraft_generators/site/templates/environments.yml.erb +4 -0
  76. data/watircraft_generators/site/templates/site.rb.erb +10 -0
  77. data/watircraft_generators/spec/USAGE +8 -0
  78. data/watircraft_generators/spec/spec_generator.rb +54 -0
  79. data/watircraft_generators/spec/templates/spec.rb.erb +17 -0
  80. data/watircraft_generators/steps/USAGE +13 -0
  81. data/watircraft_generators/steps/steps_generator.rb +62 -0
  82. data/watircraft_generators/steps/templates/steps.rb.erb +12 -0
  83. metadata +229 -0
@@ -0,0 +1,24 @@
1
+ # instance_exec comes with >1.8.7 thankfully
2
+ if VERSION <= '1.8.6'
3
+ class Object
4
+ module InstanceExecHelper; end
5
+ include InstanceExecHelper
6
+ # instance_exec method evaluates a block of code relative to the specified object, with parameters whom come from outside the object.
7
+ def instance_exec(*args, &block)
8
+ begin
9
+ old_critical, Thread.critical = Thread.critical, true
10
+ n = 0
11
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
12
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
13
+ ensure
14
+ Thread.critical = old_critical
15
+ end
16
+ begin
17
+ ret = send(mname, *args)
18
+ ensure
19
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
20
+ end
21
+ ret
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+
4
+ class String
5
+ # pluralizes a string and turns it into a symbol
6
+ # Example:
7
+ # "apple".pluralize_to_sym # => :apples
8
+ def pluralize_to_sym
9
+ self.pluralize.to_sym
10
+ end
11
+ # Opposite of humanize. Converts to lower case and converts spaces to underscores.
12
+ # Example:
13
+ # "Add Book".computerize # => "add_book"
14
+ def computerize
15
+ self.underscore.downcase.gsub ' ', '_'
16
+ end
17
+ end
@@ -0,0 +1,41 @@
1
+ # TODO: support radiogroup
2
+ # TODO: migrate this code into Watir
3
+ # TODO: also handle FireWatir classes
4
+ module Watir
5
+ class TextField # includes Hidden
6
+ def display_value
7
+ value
8
+ end
9
+ end
10
+ class FileField
11
+ def display_value
12
+ value
13
+ end
14
+ end
15
+ class CheckBox
16
+ # returns a boolean
17
+ def display_value
18
+ checked?
19
+ end
20
+ end
21
+ class SelectList
22
+ # Note: currently works for single-select only
23
+ def display_value
24
+ getSelectedItems[0]
25
+ end
26
+ end
27
+ class Button
28
+ def display_value
29
+ value
30
+ end
31
+ end
32
+ class Element
33
+ def display_value
34
+ text
35
+ end
36
+ end
37
+
38
+ class B < NonControlElement
39
+ TAG = 'B'
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ module Taza
2
+ class Browser
3
+
4
+ # Create a browser instance depending on configuration. Configuration should be read in via Taza::Settings.config.
5
+ #
6
+ # Example:
7
+ # browser = Taza::Browser.create(Taza::Settings.config)
8
+ #
9
+ def self.create(params={})
10
+ self.send("create_#{params[:driver]}".to_sym,params)
11
+ end
12
+
13
+ private
14
+
15
+ def self.create_watir(params)
16
+ require 'watir'
17
+ if params[:browser] == :ie
18
+ require 'watir/ie'
19
+ require 'extensions/watir'
20
+ end
21
+ Watir::Browser.default = params[:browser].to_s
22
+ Watir::Browser.new
23
+ end
24
+
25
+ def self.create_selenium(params)
26
+ require 'selenium'
27
+ Selenium::SeleniumDriver.new(params[:server_ip],params[:server_port],'*' + params[:browser].to_s,params[:timeout])
28
+ end
29
+
30
+ def self.create_fake(params)
31
+ FakeBrowser.new
32
+ end
33
+
34
+ end
35
+
36
+ class FakeBrowser
37
+ def goto(*args)
38
+ end
39
+ def close
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
@@ -0,0 +1,34 @@
1
+ module Taza
2
+ class Entity
3
+ #Creates a entity, pass in a hash to be methodized and the fixture to look up other fixtures (not entirely happy with this abstraction)
4
+ def initialize(hash,fixture)
5
+ @hash = hash
6
+ @fixture = fixture
7
+ define_methods_for_hash_keys
8
+ end
9
+
10
+ #This method converts hash keys into methods onto the entity
11
+ def define_methods_for_hash_keys
12
+ @hash.keys.each do |key|
13
+ create_method(key) do
14
+ get_value_for_entry(key)
15
+ end
16
+ end
17
+ end
18
+
19
+ #This method will lookup another fixture if a pluralized fixture exists otherwise return the value in the hash
20
+ def get_value_for_entry(key) # :nodoc:
21
+ if @fixture.pluralized_fixture_exists?(key)
22
+ @fixture.get_fixture_entity(key.pluralize_to_sym,@hash[key])
23
+ else
24
+ @hash[key]
25
+ end
26
+ end
27
+
28
+ private
29
+ def create_method(name, &block) # :nodoc:
30
+ self.class.send(:define_method, name, &block)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ module Taza
2
+ class Fixture # :nodoc:
3
+
4
+ def initialize # :nodoc:
5
+ @fixtures = {}
6
+ end
7
+
8
+ def load_all # :nodoc:
9
+ Dir.glob(fixtures_pattern) do |file|
10
+ entitized_fixture = {}
11
+ YAML.load_file(file).each do |key, value|
12
+ entitized_fixture[key] = value.convert_hash_keys_to_methods(self)
13
+ end
14
+ @fixtures[File.basename(file,'.yml').to_sym] = entitized_fixture
15
+ end
16
+ end
17
+
18
+ def fixture_names # :nodoc:
19
+ @fixtures.keys
20
+ end
21
+
22
+ def get_fixture_entity(fixture_file_key,entity_key) # :nodoc:
23
+ @fixtures[fixture_file_key][entity_key]
24
+ end
25
+
26
+ def pluralized_fixture_exists?(singularized_fixture_name) # :nodoc:
27
+ fixture_names.include?(singularized_fixture_name.pluralize_to_sym)
28
+ end
29
+
30
+ def fixtures_pattern # :nodoc:
31
+ File.join(base_path, 'fixtures','*.yml')
32
+ end
33
+
34
+ def base_path # :nodoc:
35
+ File.join('.','spec')
36
+ end
37
+ end
38
+
39
+ # The module that will mixin methods based on the fixture files in your 'spec/fixtures'
40
+ #
41
+ # Example:
42
+ # describe "something" do
43
+ # it "should test something" do
44
+ # users(:jane_smith).first_name.should eql("jane")
45
+ # end
46
+ # end
47
+ #
48
+ # where there is a spec/fixtures/users.yml file containing a entry of:
49
+ # jane_smith:
50
+ # first_name: jane
51
+ # last_name: smith
52
+ module Fixtures
53
+ def Fixtures.included(other_module) # :nodoc:
54
+ fixture = Fixture.new
55
+ fixture.load_all
56
+ fixture.fixture_names.each do |fixture_name|
57
+ self.class_eval do
58
+ define_method(fixture_name) do |entity_key|
59
+ fixture.get_fixture_entity(fixture_name,entity_key.to_s)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ end
data/lib/taza/flow.rb ADDED
@@ -0,0 +1,40 @@
1
+ module Taza
2
+ # Flows provide a way to write and manage common actions on a site.
3
+ # For instance, on an e-commerce site you may have multiple tests where a user is supposed to create a
4
+ # new account and add a product to the shopping bag. In this case you could have two flows. create_an_account
5
+ # and add_product_to_bag.
6
+ #
7
+ # Here's how you would get this started where your site is called Widgets of the Future:
8
+ # $ ./script/generate flow create_an_account widgets_of_the_future
9
+ # $ ./script/generate flow add_product_to_bag widgets_of_the_future
10
+ #
11
+ # This will generate flows for you in lib/sites/widgets_of_the_future/flows/
12
+ #
13
+ # From here you can create the logic needed to perform these flows without ever referencing a browser object:
14
+ #
15
+ # module WidgetsOfTheFuture
16
+ # class WidgetsOfTheFuture < Taza::Site
17
+ # def create_an_account_flow(params={})
18
+ # home_page.create_an_account_link.click
19
+ # create_an_account_page do |cap|
20
+ # cap.email.set params[:email]
21
+ # cap.password.set params[:password]
22
+ # cap.submit.click
23
+ # end
24
+ # end
25
+ # end
26
+ # end
27
+ #
28
+ #
29
+ # Then inside a spec or test you could run this flow like:
30
+ #
31
+ # describe "Widgets of the Future" do
32
+ # it "should do widgety things so that we can make more monies" do
33
+ # WidgetsOfTheFuture.new do |w|
34
+ # w.create_an_account_flow :email => "i.am@the.widget.store.com", :password => "secret"
35
+ # end
36
+ # end
37
+ # end
38
+ class Flow
39
+ end
40
+ end
data/lib/taza/page.rb ADDED
@@ -0,0 +1,259 @@
1
+ require 'extensions/string'
2
+ require 'watir/exceptions' # so we can trap them
3
+ require 'watircraft/table'
4
+
5
+ module Taza
6
+ # An abstraction of a web page, place the elements you care about accessing in here as well as specify the filters that apply when trying to access the element.
7
+ #
8
+ # Example:
9
+ # require 'taza'
10
+ # class HomePage < Taza::Page
11
+ # element(:foo) {browser.element_by_xpath('some xpath')}
12
+ # filter :title_given, :foo
13
+ #
14
+ # def title_given
15
+ # browser.title.nil?
16
+ # end
17
+ # end
18
+ #
19
+ # homepage.foo will return the element specified in the block if the filter returned true
20
+ class Page
21
+ attr_accessor :browser, :site
22
+
23
+ class << self
24
+
25
+ def elements # :nodoc:
26
+ @elements ||= {}
27
+ end
28
+
29
+ def filters # :nodoc:
30
+ @filters ||= Hash.new { [] }
31
+ end
32
+
33
+ def fields # :nodoc:
34
+ @fields ||= []
35
+ end
36
+
37
+ def url string=nil
38
+ if string.nil?
39
+ @url
40
+ else
41
+ @url = string
42
+ end
43
+ end
44
+
45
+ # An element on a page
46
+ #
47
+ # Watir Example:
48
+ # class HomePage < Taza::Page
49
+ # element(:next_button) {browser.button(:value, 'Next'}
50
+ # end
51
+ # home_page.next_button.click
52
+ def element(name, &block)
53
+ name = name.to_s.computerize.to_sym
54
+ elements[name] = block
55
+ end
56
+
57
+ # An element on a page that has a value.
58
+ # Use #field for input elements and data elements.
59
+ #
60
+ # class HomePage < Taza::Page
61
+ # field(:name) {browser.text_field(:name, 'user_name')}
62
+ # end
63
+ #
64
+ # home_page.name_field # returns the text_field element
65
+ # home_page.name_field.exists?
66
+ # home_page.name = "Fred" # calls the #set method on the text_field
67
+ # home_page.name # returns the current value (display_value) of the text_field
68
+ #
69
+ # The following Watir elements provide both #set and #display_value methods
70
+ # text_field (both text boxes and text areas)
71
+ # hidden
72
+ # file_field
73
+ # select_list
74
+ # checkbox
75
+ # (radios are the obvious item missing from this list -- we're working on it.)
76
+ #
77
+ # The following Watir elements provide #display_value methods (but not #set methods).
78
+ # button
79
+ # cell
80
+ # hidden
81
+ # all non-control elements, including divs, spans and most other elements.
82
+ def field(name, suffix='field', &block)
83
+ name = name.to_s.computerize.to_sym
84
+ fields << name
85
+ element_name = "#{name}_#{suffix}"
86
+ elements[element_name] = block
87
+ define_method(name) do
88
+ send(element_name).display_value
89
+ end
90
+ define_method("#{name}=") do |value|
91
+ send(element_name).set value
92
+ end
93
+ element_name
94
+ end
95
+
96
+ # A filter for element(s) on a page
97
+ # Example:
98
+ # class HomePage < Taza::Page
99
+ # element(:foo) {browser.element_by_xpath('some xpath')}
100
+ # filter :title_given, :foo
101
+ # #a filter will apply to all elements if none are specified
102
+ # filter :some_filter
103
+ # #a filter will also apply to all elements if the symbol :all is given
104
+ # filter :another_filter, :all
105
+ #
106
+ # def some_filter
107
+ # true
108
+ # end
109
+ #
110
+ # def some_filter
111
+ # true
112
+ # end
113
+ #
114
+ # def title_given
115
+ # browser.title.nil?
116
+ # end
117
+ # end
118
+ def filter(method_name, *elements)
119
+ elements = [:all] if elements.empty?
120
+ elements.each do |element|
121
+ self.filters[element] = self.filters[element] << method_name
122
+ end
123
+ end
124
+
125
+ # Provides access to a WatirCraft::Table.
126
+ #
127
+ # Requires that an element also be declared on the page that directly
128
+ # wraps the table element itself.
129
+ #
130
+ # Example definition
131
+ #
132
+ # class YourCartPage < ::Taza::Page
133
+ # element(:items_table) {@browser.table(:index, 1)}
134
+ # table(:items) do
135
+ # field(:quantity) {@row.cell(:index, 1)}
136
+ # field(:description) {@row.cell(:index, 2)}
137
+ # end
138
+ # field(:total) {@browser.cell(:id, 'totalcell')}
139
+ #
140
+ # Example usage
141
+ #
142
+ # your_cart_page.items.row(:description => 'Pragmatic Project Automation').quantity.should == '1'
143
+ #
144
+ # Technical details: this method creates a
145
+ # subclass and then allows its class methods to define fields and
146
+ # elements on the table.
147
+ def table(name, &block)
148
+ # create subclass for the table
149
+ sub_class = Class.new(WatirCraft::Table)
150
+ sub_class.class_eval &block
151
+ # add method to the page, it returns an instance of the table subclass
152
+ define_method(name) do
153
+ sub_class.new send("#{name}_table")
154
+ end
155
+ end
156
+ end
157
+
158
+ def initialize
159
+ add_element_methods
160
+ @active_filters = []
161
+ end
162
+
163
+ def add_element_methods # :nodoc:
164
+ self.class.elements.each do |element_name,element_block|
165
+ filters = self.class.filters[element_name] + self.class.filters[:all]
166
+ add_element_method(:filters => filters, :element_name => element_name, :element_block => element_block)
167
+ end
168
+ end
169
+
170
+ def add_element_method(params) # :nodoc:
171
+ self.class.class_eval do
172
+ define_method(params[:element_name]) do |*args|
173
+ check_filters(params)
174
+ self.instance_exec(*args,&params[:element_block])
175
+ end
176
+ end
177
+ end
178
+
179
+ def check_filters(params) # :nodoc:
180
+ params[:filters].each do |filter_method|
181
+ unless @active_filters.include?(filter_method)
182
+ @active_filters << filter_method
183
+ raise FilterError, "#{filter_method} returned false for #{params[:element_name]}" unless send(filter_method)
184
+ @active_filters.delete(filter_method)
185
+ end
186
+ end
187
+ end
188
+
189
+ # Go to this page. Url is computed based the page url and the url from the
190
+ # Site & Settings.
191
+ def goto
192
+ @site.goto self.class.url
193
+ end
194
+
195
+ # Return the full url expected for the page, taking into account the Site
196
+ # and settings.
197
+ def full_url
198
+ File.join(@site.origin, self.class.url)
199
+ end
200
+
201
+ # Enter values into fields on the page using a hash, using the key of
202
+ # each pair to name the field.
203
+ def populate hash
204
+ hash.each do |key, value|
205
+ send "#{key}=", value
206
+ end
207
+ end
208
+
209
+ # Verify that the fields specified by the keys in the hash correspond to the
210
+ # provided values.
211
+ def validate hash
212
+ hash.each do |key, value|
213
+ send(key).should == value
214
+ end
215
+ end
216
+
217
+ # Return the names of the elements defined for the page.
218
+ def elements
219
+ self.class.elements.keys.map &:to_s
220
+ end
221
+
222
+ # Return the names of the fields defined for the page.
223
+ def fields
224
+ self.class.fields.map &:to_s
225
+ end
226
+
227
+ # Returns a hash with the names and values of the specified fields.
228
+ # If no fields are specifieds, all fields on the page are used.
229
+ def values field_names=fields
230
+ result = {}
231
+ field_names.each { |name| result[name.to_sym] = send(name)}
232
+ result
233
+ end
234
+
235
+ include Watir::Exception
236
+ def element_exist? name
237
+ begin
238
+ send(name).exist?
239
+ rescue UnknownFrameException, UnknownObjectException,
240
+ UnknownFormException, UnknownCellException
241
+ false
242
+ end
243
+ end
244
+ alias :element_exists? :element_exist?
245
+
246
+ def elements_exist? element_names=elements
247
+ result = {}
248
+ element_names.each do |element|
249
+ result[element.to_sym] = element_exist?(element)
250
+ end
251
+ result
252
+ end
253
+ alias :elements_exists? :elements_exist?
254
+
255
+ end
256
+
257
+ class FilterError < StandardError; end
258
+
259
+ end
@@ -0,0 +1,80 @@
1
+ require 'activesupport'
2
+
3
+ module Taza
4
+ class Settings
5
+ # The config settings from the site.yml and config.yml files.
6
+ # ENV variables will override the settings.
7
+ #
8
+ # Example:
9
+ # Taza::Settings.config('google')
10
+ def self.config(site_name=nil)
11
+ env_settings = {}
12
+ keys = %w(browser driver timeout server_ip server_port)
13
+ keys.each do |key|
14
+ env_settings[key.to_sym] = ENV[key.upcase] if ENV[key.upcase]
15
+ end
16
+
17
+ default_settings = {:browser => :firefox, :driver => :watir}
18
+
19
+ # Because of the way #merge works, the settings at the bottom of the list
20
+ # trump those at the top.
21
+ settings = environment_settings.merge(
22
+ default_settings.merge(
23
+ config_file.merge(
24
+ env_settings)))
25
+
26
+ settings[:browser] = settings[:browser].to_sym
27
+ settings[:driver] = settings[:driver].to_sym
28
+ settings
29
+ end
30
+
31
+ # Returns a hash corresponding to the project config file.
32
+ def self.config_file
33
+ if File.exists?(config_file_path)
34
+ hash = YAML.load_file(config_file_path)
35
+ else
36
+ hash = {}
37
+ end
38
+ self.convert_string_keys_to_symbols hash
39
+ end
40
+
41
+ def self.config_file_path # :nodoc:
42
+ File.join(path, 'config', 'config.yml')
43
+ end
44
+
45
+ # Returns a hash for the currently specified test environment
46
+ def self.environment_settings # :nodoc:
47
+ file = File.join(path, environment_file)
48
+ hash_of_hashes = YAML.load_file(file)
49
+ unless hash_of_hashes.has_key? environment
50
+ raise "Environment #{environment} not found in #{file}"
51
+ end
52
+ convert_string_keys_to_symbols hash_of_hashes[environment]
53
+ end
54
+
55
+ @@root = nil
56
+
57
+ def self.path # :nodoc:
58
+ @@root || APP_ROOT
59
+ end
60
+
61
+ def self.path= path
62
+ @@root = path
63
+ end
64
+
65
+ def self.environment
66
+ ENV['ENVIRONMENT'] || 'test'
67
+ end
68
+
69
+ def self.convert_string_keys_to_symbols hash
70
+ returning Hash.new do |new_hash|
71
+ hash.each_pair {|k, v| new_hash[k.to_sym] = v}
72
+ end
73
+ end
74
+
75
+ private
76
+ def self.environment_file
77
+ 'config/environments.yml'
78
+ end
79
+ end
80
+ end