my_enginery 0.2.8

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 (118) hide show
  1. data/.gitignore +18 -0
  2. data/.travis.yml +9 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +19 -0
  6. data/README.md +957 -0
  7. data/Rakefile +48 -0
  8. data/app/base/.pryrc +1 -0
  9. data/app/base/Gemfile +25 -0
  10. data/app/base/Rakefile +4 -0
  11. data/app/base/app.rb +8 -0
  12. data/app/base/base/boot.rb +45 -0
  13. data/app/base/base/config.rb +127 -0
  14. data/app/base/base/controllers/rear-controllers/.gitkeep +0 -0
  15. data/app/base/base/database.rb +3 -0
  16. data/app/base/base/helpers/application_helpers.rb +3 -0
  17. data/app/base/base/migrations/.gitkeep +0 -0
  18. data/app/base/base/models/.gitkeep +0 -0
  19. data/app/base/base/specs/.gitkeep +0 -0
  20. data/app/base/base/views/.gitkeep +0 -0
  21. data/app/base/config.ru +4 -0
  22. data/app/base/config/config.yml +17 -0
  23. data/app/base/public/assets/Enginery.png +0 -0
  24. data/app/base/public/assets/Espresso.png +0 -0
  25. data/app/base/public/assets/application.css +13 -0
  26. data/app/base/public/assets/application.js +2 -0
  27. data/app/base/public/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
  28. data/app/base/public/assets/bootstrap/css/bootstrap.min.css +9 -0
  29. data/app/base/public/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
  30. data/app/base/public/assets/bootstrap/img/glyphicons-halflings.png +0 -0
  31. data/app/base/public/assets/bootstrap/js/bootstrap.min.js +6 -0
  32. data/app/base/public/assets/jquery.js +6 -0
  33. data/app/base/var/db/.gitkeep +0 -0
  34. data/app/base/var/log/.gitkeep +0 -0
  35. data/app/base/var/pid/.gitkeep +0 -0
  36. data/app/database/ActiveRecord.rb +11 -0
  37. data/app/database/DataMapper.rb +11 -0
  38. data/app/database/Sequel.rb +11 -0
  39. data/app/database/mysql.yml +24 -0
  40. data/app/database/postgres.yml +24 -0
  41. data/app/database/sqlite.yml +24 -0
  42. data/app/gemfiles/ActiveRecord.rb +1 -0
  43. data/app/gemfiles/BlueCloth.rb +1 -0
  44. data/app/gemfiles/DataMapper.rb +1 -0
  45. data/app/gemfiles/FastCGI.rb +1 -0
  46. data/app/gemfiles/Puma.rb +1 -0
  47. data/app/gemfiles/RDiscount.rb +1 -0
  48. data/app/gemfiles/RDoc.rb +1 -0
  49. data/app/gemfiles/RedCloth.rb +1 -0
  50. data/app/gemfiles/WEBrick.rb +1 -0
  51. data/app/gemfiles/WikiCloth.rb +1 -0
  52. data/app/gemfiles/mysql/ActiveRecord.rb +1 -0
  53. data/app/gemfiles/mysql/DataMapper.rb +1 -0
  54. data/app/gemfiles/mysql/Sequel.rb +1 -0
  55. data/app/gemfiles/postgres/ActiveRecord.rb +1 -0
  56. data/app/gemfiles/postgres/DataMapper.rb +1 -0
  57. data/app/gemfiles/postgres/Sequel.rb +1 -0
  58. data/app/gemfiles/sqlite/ActiveRecord.rb +1 -0
  59. data/app/gemfiles/sqlite/DataMapper.rb +1 -0
  60. data/app/gemfiles/sqlite/Sequel.rb +1 -0
  61. data/app/layouts/ERB/layout.erb +56 -0
  62. data/app/layouts/Erubis/layout.erb +56 -0
  63. data/app/layouts/Haml/layout.haml +38 -0
  64. data/app/layouts/Slim/layout.slim +38 -0
  65. data/app/migrations/ActiveRecord.erb +51 -0
  66. data/app/migrations/DataMapper.erb +61 -0
  67. data/app/migrations/Sequel.erb +54 -0
  68. data/app/migrations/tracking_table/ActiveRecord.rb +19 -0
  69. data/app/migrations/tracking_table/DataMapper.rb +26 -0
  70. data/app/migrations/tracking_table/Sequel.rb +18 -0
  71. data/app/rakefiles/ActiveRecord.rb +0 -0
  72. data/app/rakefiles/DataMapper.rb +1 -0
  73. data/app/rakefiles/Sequel.rb +0 -0
  74. data/app/rakefiles/Specular.rb +1 -0
  75. data/app/specfiles/Specular.erb +7 -0
  76. data/bin/my_enginery +235 -0
  77. data/lib/enginery.rb +23 -0
  78. data/lib/enginery/cli.rb +44 -0
  79. data/lib/enginery/configurator.rb +139 -0
  80. data/lib/enginery/delete.rb +116 -0
  81. data/lib/enginery/enginery.rb +41 -0
  82. data/lib/enginery/generator.rb +325 -0
  83. data/lib/enginery/helpers/app.rb +83 -0
  84. data/lib/enginery/helpers/generic.rb +145 -0
  85. data/lib/enginery/helpers/input.rb +123 -0
  86. data/lib/enginery/helpers/orm.rb +86 -0
  87. data/lib/enginery/helpers/validations.rb +101 -0
  88. data/lib/enginery/migrator.rb +371 -0
  89. data/lib/enginery/rake-tasks/data_mapper.rb +49 -0
  90. data/lib/enginery/rake-tasks/specular.rb +41 -0
  91. data/lib/enginery/registry.rb +101 -0
  92. data/lib/enginery/usage.rb +66 -0
  93. data/lib/enginery/version.rb +7 -0
  94. data/logo.png +0 -0
  95. data/my_enginery.gemspec +37 -0
  96. data/test/delete/test__admin.rb +49 -0
  97. data/test/delete/test__controller.rb +37 -0
  98. data/test/delete/test__helper.rb +49 -0
  99. data/test/delete/test__migration.rb +27 -0
  100. data/test/delete/test__model.rb +35 -0
  101. data/test/delete/test__route.rb +35 -0
  102. data/test/delete/test__spec.rb +59 -0
  103. data/test/delete/test__view.rb +51 -0
  104. data/test/generator/test__admin.rb +39 -0
  105. data/test/generator/test__controller.rb +123 -0
  106. data/test/generator/test__helper.rb +56 -0
  107. data/test/generator/test__model.rb +206 -0
  108. data/test/generator/test__project.rb +81 -0
  109. data/test/generator/test__route.rb +110 -0
  110. data/test/generator/test__spec.rb +56 -0
  111. data/test/generator/test__view.rb +85 -0
  112. data/test/migrator/test__auto_generation.rb +41 -0
  113. data/test/migrator/test__manual_generation.rb +59 -0
  114. data/test/migrator/test__migrations.rb +139 -0
  115. data/test/sandbox/.gitkeep +0 -0
  116. data/test/setup.rb +29 -0
  117. data/test/support/spec_helpers.rb +151 -0
  118. metadata +392 -0
@@ -0,0 +1,116 @@
1
+ module Enginery
2
+ class Delete
3
+ include Helpers
4
+ attr_reader :dst_root
5
+
6
+ def initialize dst_root
7
+ @dst_root = dst_root
8
+ end
9
+
10
+ def controller name
11
+
12
+ controller_path, controller_object = valid_controller?(name)
13
+
14
+ routes_by_controller(name).each {|r| route(name, r)}
15
+ helper(name)
16
+
17
+ if File.exists?(controller_path)
18
+ o
19
+ o '*** Removing "%s" folder ***' % unrootify(controller_path)
20
+ FileUtils.rm_r(controller_path)
21
+ end
22
+
23
+ file = controller_path + CONTROLLER_SUFFIX
24
+ if File.exists?(file)
25
+ o '*** Deleting "%s" file ***' % unrootify(file)
26
+ o
27
+ FileUtils.rm(file)
28
+ end
29
+
30
+ end
31
+
32
+ def rear_controller model
33
+
34
+ file = dst_path(:rear_controllers, class_to_route(model) + ADMIN_SUFFIX)
35
+
36
+ if File.exists?(file)
37
+ o
38
+ o '*** Deleting "%s" file ***' % unrootify(file)
39
+ FileUtils.rm(file)
40
+ end
41
+ end
42
+
43
+ def route controller, name
44
+ file, * = valid_route?(controller, name)
45
+ if File.exists?(file)
46
+ o '*** Deleting "%s" file ***' % unrootify(file)
47
+ o
48
+ FileUtils.rm(file)
49
+ end
50
+ view controller, name
51
+ spec controller, name
52
+ end
53
+
54
+ def view controller, route
55
+ _, ctrl = valid_controller?(controller)
56
+ path, ext = view_setups_for(ctrl, route)
57
+ file = File.join(path, route + ext)
58
+
59
+ return unless File.exists?(file)
60
+ o '*** Deleting "%s" file ***' % unrootify(file)
61
+ o
62
+ FileUtils.rm(file)
63
+ end
64
+
65
+ def spec controller, route
66
+ _, controller_object = valid_controller?(controller)
67
+ _, route = valid_route?(controller, route)
68
+
69
+ path = dst_path(:specs, class_to_route(controller), '/')
70
+ file = path + route + SPEC_SUFFIX
71
+ return unless File.exists?(file)
72
+ o '*** Deleting "%s" file ***' % unrootify(file)
73
+ o
74
+ FileUtils.rm(file)
75
+ end
76
+
77
+ def helper controller
78
+ _, ctrl = valid_controller?(controller)
79
+ file = dst_path(:helpers, class_to_route(controller) + HELPER_SUFFIX)
80
+ return unless File.exists?(file)
81
+ o '*** Deleting "%s" file ***' % unrootify(file)
82
+ o
83
+ FileUtils.rm(file)
84
+ end
85
+
86
+ def model name
87
+ name.nil? || name.empty? && fail("Please provide model name")
88
+
89
+ file = dst_path(:models, class_to_route(name) + MODEL_SUFFIX)
90
+ if File.exists?(file)
91
+ o '*** Deleting "%s" file ***' % unrootify(file)
92
+ o
93
+ FileUtils.rm(file)
94
+ end
95
+ migrations_by_model(name).each do |m|
96
+ migration m.split('.').first
97
+ end
98
+ rear_controller(name)
99
+ true
100
+ end
101
+
102
+ def migration name
103
+ name.nil? || name.empty? && fail("Please provide migration name")
104
+ Dir[dst_path(:migrations, '**/%s.*%s' % [name, MIGRATION_SUFFIX])].each do |file|
105
+ load_file file
106
+ if defined?(DataMapper::Migration) && Migrator::MigratorInstance.instance_of?(DataMapper::Migration)
107
+ Migrator.new(dst_root).update_model_file(Migrator::MigratorContext, :down)
108
+ end
109
+ o '*** Deleting "%s" file ***' % unrootify(file)
110
+ o
111
+ FileUtils.rm(file)
112
+ end
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,41 @@
1
+ module Enginery
2
+ ENVIRONMENTS = [:development, :test, :production].freeze
3
+ DEFAULT_TEST_FRAMEWORK = :Specular
4
+ DEFAULT_DB_TYPE = :sqlite
5
+ INDENT = (' ' * 2).freeze
6
+ KNOWN_WEB_SERVERS = %w[
7
+ WEBrick
8
+ Thin
9
+ Unicorn
10
+ Rainbows
11
+ Puma
12
+ Reel
13
+ Mongrel
14
+ FastCGI
15
+ SCGI
16
+ ].map(&:to_sym).freeze
17
+
18
+ # using z_ prefix to make sure tracking table shown last
19
+ # when some database management tools used.
20
+ TRACKING_TABLE = :z_enginery_migrator_tracks
21
+ TRACKING_TABLE__COLUMNS = [:migration, :performed_at, :vector].freeze
22
+ TRACKING_TABLE__INDEXES = [:migration].freeze
23
+
24
+ CONTROLLER_SUFFIX = '_controller.rb'.freeze
25
+ ROUTE_SUFFIX = '.rb'.freeze
26
+ SPEC_SUFFIX = '_spec.rb'.freeze
27
+ MODEL_SUFFIX = '.rb'.freeze
28
+ MIGRATION_SUFFIX = '.rb'.freeze
29
+ HELPER_SUFFIX = '.rb'.freeze
30
+ ADMIN_SUFFIX = '.rb'.freeze # suffix for Rear controllers
31
+
32
+ ORM_IDENTITY_METHODS = {
33
+ ActiveRecord: [:connection, :columns, :reflect_on_all_associations].freeze,
34
+ DataMapper: [:repository, :properties, :relationships].freeze,
35
+ Sequel: [:db_schema, :columns, :dataset].freeze
36
+ }.freeze
37
+ ORM_MATCHERS = Hash[ORM_IDENTITY_METHODS.keys.zip(ORM_IDENTITY_METHODS.keys.map{|x| Regexp.new(x.to_s, 'i')})]
38
+ ORM_ASSOCIATIONS = [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many].freeze
39
+
40
+ EXECUTABLE = File.expand_path('../../../bin/enginery', __FILE__).freeze
41
+ end
@@ -0,0 +1,325 @@
1
+ module Enginery
2
+ class Generator
3
+
4
+ include Helpers
5
+ attr_reader :dst_root, :setups
6
+
7
+ def initialize dst_root, setups = {}
8
+ @dst_root, @setups = dst_root, setups
9
+ end
10
+
11
+ def generate_project name = nil
12
+ name = name.to_s
13
+
14
+ if name.empty?
15
+ name = '.'
16
+ else
17
+ name =~ /\.\.|\// && fail('Project name can not contain "/" nor ".."')
18
+ @dst_root, @dst_path_map = File.join(@dst_root, name, ''), nil
19
+ end
20
+
21
+ Dir[dst_path(:root, '*')].any? && fail('"%s" should be a empty folder' % dst_path.root)
22
+
23
+ o
24
+ o '=== Generating "%s" project ===' % name
25
+
26
+ folders, files = Dir[src_path(:base, '**/{*,.[a-z]*}')].partition do |entry|
27
+ File.directory?(entry)
28
+ end
29
+
30
+ FileUtils.mkdir_p dst_path.root
31
+ o "#{name}/"
32
+ folders.each do |folder|
33
+ path = unrootify(folder, src_path.base)
34
+ o " D #{path}/"
35
+ FileUtils.mkdir_p dst_path(:root, path)
36
+ end
37
+
38
+ files.reject {|f| File.basename(f) == '.gitkeep'}.each do |file|
39
+ path = unrootify(file, src_path.base)
40
+ o " F #{path}"
41
+ FileUtils.cp file, dst_path(:root, path)
42
+ end
43
+
44
+ Configurator.new dst_root, setups do
45
+ update_gemfile
46
+ update_rakefile
47
+ update_boot_rb
48
+ update_config_yml
49
+ update_database_rb
50
+ update_database_yml
51
+ end
52
+ name
53
+ end
54
+
55
+ def generate_controller name
56
+
57
+ name.nil? || name.empty? && fail("Please provide controller name")
58
+ before, ctrl_name, after = namespace_to_source_code(name)
59
+
60
+ source_code, i = [], INDENT * before.size
61
+ before.each {|s| source_code << s}
62
+ source_code << "#{i}class #{ctrl_name} < E"
63
+
64
+ (@setups[:include] || []).each do |mdl|
65
+ source_code << "#{i + INDENT}include #{mdl}"
66
+ end
67
+
68
+ source_code << "#{i + INDENT}include %sHelpers if defined?(%sHelpers)" % [ctrl_name, ctrl_name]
69
+ source_code << "#{i + INDENT}# controller-wide setups"
70
+
71
+ if route = setups[:route]
72
+ source_code << "#{i + INDENT}map '#{route}'"
73
+ end
74
+ if engine = setups[:engine]
75
+ source_code << "#{i + INDENT}engine :#{engine}"
76
+ Configurator.new(dst_root, engine: engine).update_gemfile
77
+ end
78
+ if format = setups[:format]
79
+ source_code << "#{i + INDENT}format '#{format}'"
80
+ end
81
+ source_code << INDENT
82
+
83
+ source_code << "#{i}end"
84
+ after.each {|s| source_code << s}
85
+
86
+ path = dst_path(:controllers, class_to_route(name))
87
+ file = path + CONTROLLER_SUFFIX
88
+ File.exists?(file) && fail('"%s" controller already exists' % name)
89
+ o
90
+ o '=== Generating "%s" controller ===' % name
91
+ o '*** Creating "%s/" ***' % unrootify(path)
92
+ FileUtils.mkdir_p(path)
93
+
94
+ write_file file, source_code.join("\n")
95
+ output_source_code source_code
96
+ ctrl_name
97
+ end
98
+
99
+ def generate_route ctrl_name, name
100
+
101
+ action_file, action = valid_route?(ctrl_name, name)
102
+
103
+ File.exists?(action_file) && fail('"%s" route already exists' % name)
104
+
105
+ before, ctrl_name, after = namespace_to_source_code(ctrl_name)
106
+
107
+ source_code, i = [], ' ' * before.size
108
+ before.each {|s| source_code << s}
109
+ source_code << "#{i}class #{ctrl_name}"
110
+ source_code << "#{i + INDENT}# action-specific setups"
111
+ source_code << ''
112
+
113
+ if format = setups[:format]
114
+ source_code << "#{i + INDENT}format_for :#{action}, '#{format}'"
115
+ end
116
+ if setups.reject {|k,v| k == :route}.any?
117
+ source_code << "#{i + INDENT}before :#{action} do"
118
+ if engine = setups[:engine]
119
+ source_code << "#{i + INDENT*2}engine :#{engine}"
120
+ Configurator.new(dst_root, engine: engine).update_gemfile
121
+ end
122
+ source_code << "#{i + INDENT}end"
123
+ source_code << ""
124
+ end
125
+
126
+ source_code << (i + INDENT + "def #{action}")
127
+ action_source_code = ["render"]
128
+ if block_given?
129
+ action_source_code = yield
130
+ action_source_code.is_a?(Array) || action_source_code = [action_source_code]
131
+ end
132
+ action_source_code.each do |line|
133
+ source_code << (i + INDENT*2 + line.to_s)
134
+ end
135
+ source_code << (i + INDENT + "end")
136
+ source_code << ''
137
+
138
+ source_code << "#{i}end"
139
+ after.each {|s| source_code << s}
140
+
141
+ o
142
+ o '=== Generating "%s" route ===' % name
143
+
144
+ write_file action_file, source_code.join("\n")
145
+ output_source_code source_code
146
+ action
147
+ end
148
+
149
+ def generate_helper ctrl_name
150
+ o
151
+ o '=== Generating helper file for "%s" controller ===' % ctrl_name
152
+
153
+ _, ctrl = valid_controller?(ctrl_name)
154
+ file = dst_path(:helpers, class_to_route(ctrl_name) + HELPER_SUFFIX)
155
+ path = File.dirname(file)
156
+
157
+ if File.exists?(path)
158
+ File.directory?(path) || fail('"%s" should be a directory' % unrootify(path))
159
+ else
160
+ o '*** Creating "%s/" ***' % unrootify(path)
161
+ FileUtils.mkdir_p(path)
162
+ end
163
+
164
+ if File.exists?(file)
165
+ File.file?(file) || fail('"%s" should be a file' % unrootify(file))
166
+ o('*** WARN: overwriting "%s" file ***' % unrootify(file))
167
+ else
168
+ o '*** Creating "%s" ***' % unrootify(file)
169
+ end
170
+
171
+ before, helper_name, after = namespace_to_source_code(ctrl_name)
172
+
173
+ source_code, i = [], INDENT * before.size
174
+ before.each {|s| source_code << s}
175
+ source_code << "#{i}module #{helper_name}Helpers"
176
+
177
+ source_code << "#{i + INDENT}include Helpers if defined?(Helpers)"
178
+ source_code << ""
179
+ source_code << "#{i + INDENT}# helper methods for %s controller" % ctrl_name
180
+ source_code << INDENT
181
+
182
+ source_code << "#{i}end"
183
+ after.each {|s| source_code << s}
184
+
185
+ write_file file, source_code.join("\n")
186
+ output_source_code source_code
187
+ file
188
+ end
189
+
190
+ def generate_view ctrl_name, name
191
+
192
+ _, action = valid_route?(ctrl_name, name)
193
+ _, ctrl = valid_controller?(ctrl_name)
194
+ path, ext = view_setups_for(ctrl, action)
195
+
196
+ o
197
+ o '=== Generating "%s" view ===' % name
198
+ if File.exists?(path)
199
+ File.directory?(path) || fail('"%s" should be a directory' % unrootify(path))
200
+ else
201
+ o '*** Creating "%s/" ***' % unrootify(path)
202
+ FileUtils.mkdir_p(path)
203
+ end
204
+ file = File.join(path, action + ext)
205
+ if File.exists?(file)
206
+ File.file?(file) || fail('"%s" should be a file' % unrootify(file))
207
+ o('*** WARN: overwriting "%s" file ***' % unrootify(file))
208
+ else
209
+ o '*** Creating "%s" ***' % unrootify(file)
210
+ end
211
+ File.open(file, 'w') {}
212
+ file
213
+ end
214
+
215
+ def generate_model name
216
+
217
+ name.nil? || name.empty? && fail("Please provide model name")
218
+ before, model_name, after = namespace_to_source_code(name)
219
+
220
+ superclass, insertions = '', []
221
+ if orm = valid_orm?(setups[:orm] || Cfg[:orm])
222
+ orm == :ActiveRecord && superclass = ' < ActiveRecord::Base'
223
+ orm == :Sequel && superclass = ' < Sequel::Model'
224
+ orm == :DataMapper && insertions << 'include DataMapper::Resource'
225
+
226
+ (@setups[:include] || []).each do |mdl|
227
+ insertions << "include #{mdl}"
228
+ end
229
+ insertions << ''
230
+
231
+ orm == :DataMapper && insertions << 'property :id, Serial'
232
+ insertions << ''
233
+ send(orm.to_s.downcase + '_associations', @setups).each do |a|
234
+ insertions << a
235
+ end
236
+ end
237
+ insertions << ''
238
+
239
+ source_code, i = [], INDENT * before.size
240
+ before.each {|s| source_code << s}
241
+ source_code << "#{i}class #{model_name + superclass}"
242
+
243
+ insertions.each do |line|
244
+ source_code << (i + INDENT + line.to_s)
245
+ end
246
+
247
+ source_code << "#{i}end"
248
+ after.each {|s| source_code << s}
249
+ source_code = source_code.join("\n")
250
+
251
+ file = dst_path(:models, class_to_route(name) + '.rb')
252
+ File.exists?(file) && fail('"%s" file already exists' % unrootify(file))
253
+
254
+ o
255
+ o '=== Generating "%s" model ===' % name
256
+ dir = File.dirname(file)
257
+ if File.exists?(dir)
258
+ File.directory?(dir) || fail("#{unrootify dir} should be a directory")
259
+ else
260
+ o '*** Creating "%s/" ***' % unrootify(dir)
261
+ FileUtils.mkdir_p(dir)
262
+ end
263
+
264
+ write_file file, source_code
265
+ output_source_code source_code.split("\n")
266
+ model_name
267
+ end
268
+
269
+ def generate_rear_controller model
270
+
271
+ model.nil? || model.empty? && fail('Please provide the model name to build controller for')
272
+ model_exists?(model) || fail('Seems "%s" model does not exists' % model)
273
+
274
+ source_code = []
275
+ source_code << "defined?(Rear) && Rear.register('#{model}') do"
276
+ source_code << " # Rear setups for #{model} model"
277
+ source_code << " # Details at https://github.com/espresso/rear"
278
+ source_code << " "
279
+ source_code << " "
280
+ source_code << "end"
281
+ source_code << ""
282
+
283
+ path = dst_path(:rear_controllers, class_to_route(model))
284
+ file = path + ADMIN_SUFFIX
285
+ File.exists?(file) && fail('A Rear controller for "%s" model already exists' % model)
286
+ o
287
+ o '=== Generating Rear controller for "%s" model ===' % model
288
+ FileUtils.mkdir_p(File.dirname(path))
289
+
290
+ write_file file, source_code.join("\n")
291
+ output_source_code source_code
292
+ model
293
+ end
294
+
295
+ def generate_spec ctrl_name, name
296
+
297
+ context = {}
298
+ _, context[:controller] = valid_controller?(ctrl_name)
299
+ _, context[:action] = valid_route?(ctrl_name, name)
300
+ context[:spec] = [ctrl_name, context[:action]]*'#'
301
+
302
+ o
303
+ o '=== Generating "%s#%s" spec ===' % [ctrl_name, name]
304
+ path = dst_path(:specs, class_to_route(ctrl_name), '/')
305
+ if File.exists?(path)
306
+ File.directory?(path) || fail("#{path} should be a directory")
307
+ else
308
+ o '*** Creating "%s" ***' % unrootify(path)
309
+ FileUtils.mkdir_p(path)
310
+ end
311
+
312
+ file = path + context[:action] + SPEC_SUFFIX
313
+ File.exists?(file) && o('*** WARN: overwriting "%s" file ***' % unrootify(file))
314
+
315
+ test_framework = setups[:test_framework] || DEFAULT_TEST_FRAMEWORK
316
+ engine = Tenjin::Engine.new(path: [src_path.specfiles], cache: false)
317
+ source_code = engine.render(test_framework.to_s + '.erb', context)
318
+
319
+ write_file file, source_code
320
+ output_source_code source_code.split("\n")
321
+ file
322
+ end
323
+
324
+ end
325
+ end