lotusrb 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -14
  3. data/FEATURES.md +21 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +8 -7
  6. data/bin/lotus +1 -0
  7. data/lib/lotus.rb +6 -0
  8. data/lib/lotus/action/csrf_protection.rb +1 -1
  9. data/lib/lotus/application.rb +4 -4
  10. data/lib/lotus/application_name.rb +8 -6
  11. data/lib/lotus/cli.rb +75 -66
  12. data/lib/lotus/cli_sub_commands/assets.rb +29 -0
  13. data/lib/lotus/{commands → cli_sub_commands}/db.rb +24 -29
  14. data/lib/lotus/cli_sub_commands/destroy.rb +102 -0
  15. data/lib/lotus/cli_sub_commands/generate.rb +127 -0
  16. data/lib/lotus/commands/assets/precompile.rb +35 -0
  17. data/lib/lotus/commands/console.rb +28 -8
  18. data/lib/lotus/commands/db/abstract.rb +6 -2
  19. data/lib/lotus/commands/db/console.rb +5 -5
  20. data/lib/lotus/commands/generate/abstract.rb +63 -0
  21. data/lib/lotus/commands/generate/action.rb +262 -0
  22. data/lib/lotus/commands/generate/app.rb +116 -0
  23. data/lib/lotus/commands/generate/mailer.rb +118 -0
  24. data/lib/lotus/commands/generate/migration.rb +63 -0
  25. data/lib/lotus/commands/generate/model.rb +96 -0
  26. data/lib/lotus/commands/new/abstract.rb +128 -0
  27. data/lib/lotus/commands/new/app.rb +116 -0
  28. data/lib/lotus/commands/new/container.rb +102 -0
  29. data/lib/lotus/commands/routes.rb +19 -2
  30. data/lib/lotus/commands/server.rb +16 -2
  31. data/lib/lotus/config/framework_configuration.rb +13 -1
  32. data/lib/lotus/configuration.rb +24 -76
  33. data/lib/lotus/container.rb +15 -2
  34. data/lib/lotus/environment.rb +43 -4
  35. data/lib/lotus/frameworks.rb +1 -0
  36. data/lib/lotus/generators/action/action_spec.minitest.tt +1 -1
  37. data/lib/lotus/generators/action/action_spec.rspec.tt +1 -1
  38. data/lib/lotus/generators/action/view_spec.minitest.tt +1 -1
  39. data/lib/lotus/generators/action/view_spec.rspec.tt +1 -1
  40. data/lib/lotus/generators/{slice → app}/.gitkeep.tt +0 -0
  41. data/lib/lotus/generators/{slice → app}/application.rb.tt +61 -23
  42. data/lib/lotus/generators/app/config/initializers/.gitkeep +0 -0
  43. data/lib/lotus/generators/{slice → app}/config/routes.rb.tt +0 -0
  44. data/lib/lotus/generators/app/favicon.ico +0 -0
  45. data/lib/lotus/generators/{slice → app}/templates/application.html.erb.tt +2 -1
  46. data/lib/lotus/generators/app/views/application_layout.rb.tt +7 -0
  47. data/lib/lotus/generators/application/app/.env.development.tt +1 -0
  48. data/lib/lotus/generators/application/app/.env.test.tt +1 -0
  49. data/lib/lotus/generators/application/app/Gemfile.tt +1 -0
  50. data/lib/lotus/generators/application/app/Rakefile.minitest.tt +1 -0
  51. data/lib/lotus/generators/application/app/Rakefile.rspec.tt +1 -0
  52. data/lib/lotus/generators/application/app/config/application.rb.tt +56 -18
  53. data/lib/lotus/generators/application/app/config/initializers/.gitkeep +0 -0
  54. data/lib/lotus/generators/application/app/favicon.ico +0 -0
  55. data/lib/lotus/generators/application/app/gitignore.tt +2 -0
  56. data/lib/lotus/generators/application/app/lotusrc.tt +3 -3
  57. data/lib/lotus/generators/application/app/spec_helper.rb.rspec.tt +5 -1
  58. data/lib/lotus/generators/application/app/templates/application.html.erb.tt +1 -0
  59. data/lib/lotus/generators/application/container/.env.development.tt +1 -0
  60. data/lib/lotus/generators/application/container/.env.test.tt +1 -0
  61. data/lib/lotus/generators/application/container/Gemfile.tt +1 -1
  62. data/lib/lotus/generators/application/container/Rakefile.minitest.tt +1 -0
  63. data/lib/lotus/generators/application/container/Rakefile.rspec.tt +1 -0
  64. data/lib/lotus/generators/application/container/config/initializers/.gitkeep +0 -0
  65. data/lib/lotus/generators/application/container/gitignore.tt +2 -0
  66. data/lib/lotus/generators/application/container/lib/chirp/mailers/.gitkeep +0 -0
  67. data/lib/lotus/generators/application/container/lib/chirp/mailers/templates/.gitkeep +0 -0
  68. data/lib/lotus/generators/application/container/lotusrc.tt +3 -3
  69. data/lib/lotus/generators/application/container/spec_helper.rb.rspec.tt +5 -1
  70. data/lib/lotus/generators/database_config.rb +17 -4
  71. data/lib/lotus/generators/generatable.rb +51 -0
  72. data/lib/lotus/generators/generator.rb +35 -0
  73. data/lib/lotus/generators/test_framework.rb +42 -0
  74. data/lib/lotus/loader.rb +43 -0
  75. data/lib/lotus/lotusrc.rb +50 -49
  76. data/lib/lotus/middleware.rb +28 -10
  77. data/lib/lotus/rake_helper.rb +68 -0
  78. data/lib/lotus/rake_tasks.rb +2 -0
  79. data/lib/lotus/rendering_policy.rb +2 -2
  80. data/lib/lotus/repositories/car_repository.rb +3 -0
  81. data/lib/lotus/repositories/name_repository.rb +3 -0
  82. data/lib/lotus/setup.rb +1 -3
  83. data/lib/lotus/static.rb +77 -0
  84. data/lib/lotus/templates/welcome.html.erb +7 -4
  85. data/lib/lotus/version.rb +1 -1
  86. data/lib/lotus/views/default.rb +2 -1
  87. data/lib/lotus/views/default_template_finder.rb +4 -3
  88. data/lotusrb.gemspec +11 -9
  89. metadata +79 -60
  90. data/lib/lotus/commands/generate.rb +0 -97
  91. data/lib/lotus/commands/new.rb +0 -43
  92. data/lib/lotus/config/assets.rb +0 -81
  93. data/lib/lotus/generators/abstract.rb +0 -31
  94. data/lib/lotus/generators/action.rb +0 -235
  95. data/lib/lotus/generators/app.rb +0 -39
  96. data/lib/lotus/generators/app/.gitkeep +0 -1
  97. data/lib/lotus/generators/application/app.rb +0 -129
  98. data/lib/lotus/generators/application/container.rb +0 -111
  99. data/lib/lotus/generators/mailer.rb +0 -112
  100. data/lib/lotus/generators/migration.rb +0 -58
  101. data/lib/lotus/generators/model.rb +0 -90
  102. data/lib/lotus/generators/slice.rb +0 -94
  103. data/lib/lotus/generators/slice/views/application_layout.rb.tt +0 -7
@@ -2,8 +2,12 @@ module Lotus
2
2
  module Commands
3
3
  class DB
4
4
  class Abstract
5
- def initialize(environment)
6
- environment.require_application_environment
5
+ attr_reader :environment
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ @environment = Lotus::Environment.new(options)
10
+ @environment.require_application_environment
7
11
  end
8
12
 
9
13
  def start
@@ -1,16 +1,16 @@
1
1
  require 'lotus/utils/class'
2
+ require 'lotus/commands/db/abstract'
2
3
 
3
4
  module Lotus
4
5
  module Commands
5
6
  class DB
6
- class Console
7
- attr_reader :name, :env_options, :environment
7
+ class Console < Abstract
8
+ attr_reader :name, :env_options
8
9
 
9
- def initialize(name, environment)
10
+ def initialize(options, name)
11
+ super(options)
10
12
  @name = name
11
- @environment = environment
12
13
  @env_options = environment.to_options
13
- @environment.require_application_environment
14
14
  end
15
15
 
16
16
  def start
@@ -0,0 +1,63 @@
1
+ require 'lotus/environment'
2
+ require 'lotus/generators/generatable'
3
+ require 'lotus/generators/test_framework'
4
+ require 'lotus/version'
5
+ require 'lotus/utils/string'
6
+
7
+ module Lotus
8
+ module Commands
9
+ class Generate
10
+ class Abstract
11
+
12
+ include Lotus::Generators::Generatable
13
+
14
+ attr_reader :options, :target_path
15
+
16
+ def initialize(options)
17
+ @options = Lotus::Utils::Hash.new(options).symbolize!
18
+ assert_options!
19
+
20
+ @target_path = Pathname.pwd
21
+ end
22
+
23
+ def template_source_path
24
+ generator = self.class.name.split('::').last.downcase
25
+ Pathname.new(::File.dirname(__FILE__) + "/../../generators/#{generator}/").realpath
26
+ end
27
+
28
+ private
29
+
30
+ def test_framework
31
+ @test_framework ||= Lotus::Generators::TestFramework.new(lotusrc, options[:test])
32
+ end
33
+
34
+ def lotusrc_options
35
+ lotusrc.options
36
+ end
37
+
38
+ def lotusrc
39
+ @lotusrc ||= Lotusrc.new(target_path)
40
+ end
41
+
42
+ def environment
43
+ @environment ||= Lotus::Environment.new(options)
44
+ end
45
+
46
+ def template_engine
47
+ options.fetch(:template, default_template_engine)
48
+ end
49
+
50
+ def default_template_engine
51
+ lotusrc_options.fetch(:template)
52
+ end
53
+
54
+ def assert_options!
55
+ if options.nil?
56
+ raise ArgumentError.new('options must not be nil')
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,262 @@
1
+ require 'lotus/commands/generate/abstract'
2
+ require 'lotus/routing/route'
3
+
4
+ module Lotus
5
+ module Commands
6
+ class Generate
7
+ class Action < Abstract
8
+
9
+ # @since 0.5.0
10
+ # @api private
11
+ ACTION_SEPARATOR = /[\/,#]/
12
+
13
+ # @since 0.5.0
14
+ # @api private
15
+ ROUTE_ENDPOINT_SEPARATOR = '#'.freeze
16
+
17
+ # @since 0.5.0
18
+ # @api private
19
+ QUOTED_NAME = /(\"|\'|\\)/
20
+
21
+ # Default HTTP method used when generating an action.
22
+ #
23
+ # @since 0.5.0
24
+ # @api private
25
+ DEFAULT_HTTP_METHOD = 'GET'.freeze
26
+
27
+ # HTTP methods used when generating resourceful actions.
28
+ #
29
+ # @since 0.6.0
30
+ # @api private
31
+ RESOURCEFUL_HTTP_METHODS = {
32
+ 'Create' => 'POST',
33
+ 'Update' => 'PATCH',
34
+ 'Destroy' => 'DELETE'
35
+ }.freeze
36
+
37
+ # For resourceful actions, what to add to the end of the base URL
38
+ #
39
+ # @since 0.6.0
40
+ # @api private
41
+ RESOURCEFUL_ROUTE_URL_SUFFIXES = {
42
+ 'Show' => '/:id',
43
+ 'Update' => '/:id',
44
+ 'Destroy' => '/:id',
45
+ 'New' => '/new',
46
+ 'Edit' => '/:id/edit',
47
+ }.freeze
48
+
49
+ def initialize(options, application_name, controller_and_action_name)
50
+ super(options)
51
+ if !environment.container?
52
+ application_name = File.basename(Dir.pwd)
53
+ end
54
+
55
+ controller_and_action_name = Utils::String.new(controller_and_action_name).underscore.gsub(QUOTED_NAME, '')
56
+
57
+ *controller_name, action_name = controller_and_action_name.split(ACTION_SEPARATOR)
58
+
59
+ @application_name = Utils::String.new(application_name).classify
60
+ @controller_name = Utils::String.new(controller_name.join("/")).classify
61
+ @action_name = Utils::String.new(action_name).classify
62
+ @controller_pathname = Utils::String.new(@controller_name).underscore
63
+
64
+ assert_application_name!
65
+ assert_controller_name!
66
+ assert_action_name!
67
+ assert_http_method!
68
+ end
69
+
70
+ def map_templates
71
+ add_mapping("action_spec.#{test_framework.framework}.tt", action_spec_path)
72
+
73
+ if skip_view?
74
+ add_mapping('action_without_view.rb.tt', action_path)
75
+ else
76
+ add_mapping('action.rb.tt', action_path)
77
+ add_mapping('view.rb.tt', view_path)
78
+ add_mapping('template.tt', template_path)
79
+ add_mapping("view_spec.#{test_framework.framework}.tt", view_spec_path)
80
+ end
81
+ end
82
+
83
+ def post_process_templates
84
+ generate_route
85
+ end
86
+
87
+ # @since 0.5.0
88
+ # @api private
89
+ def template_options
90
+ {
91
+ app: @application_name,
92
+ controller: @controller_name,
93
+ action: @action_name,
94
+ relative_action_path: relative_action_path,
95
+ relative_view_path: relative_view_path,
96
+ template_path: template_path,
97
+ }
98
+ end
99
+
100
+ private
101
+
102
+ # @since 0.5.0
103
+ # @api private
104
+ def generate_route
105
+ routes_path.dirname.mkpath
106
+ FileUtils.touch(routes_path)
107
+
108
+ generator.prepend_to_file(routes_path, "#{ http_method } '#{ route_url }', to: '#{ route_endpoint }'\n")
109
+ end
110
+
111
+ def skip_view?
112
+ options.fetch(:skip_view, false)
113
+ end
114
+
115
+ # @since 0.5.0
116
+ # @api private
117
+ def http_method
118
+ options.fetch(:method, resourceful_http_method).downcase
119
+ end
120
+
121
+ # @since 0.6.0
122
+ # @api private
123
+ def resourceful_http_method
124
+ RESOURCEFUL_HTTP_METHODS.fetch(@action_name, DEFAULT_HTTP_METHOD)
125
+ end
126
+
127
+ # @since 0.5.0
128
+ # @api private
129
+ def route_url
130
+ options.fetch(:url, "/#{ @controller_pathname }#{ resourceful_route_url_suffix }")
131
+ end
132
+
133
+ # @since 0.6.0
134
+ # @api private
135
+ def resourceful_route_url_suffix
136
+ RESOURCEFUL_ROUTE_URL_SUFFIXES.fetch(@action_name, "")
137
+ end
138
+
139
+ # @since 0.5.0
140
+ # @api private
141
+ def route_endpoint
142
+ "#{ @controller_pathname }#{ ROUTE_ENDPOINT_SEPARATOR }#{ @action_name }".downcase
143
+ end
144
+
145
+ # @since 0.5.0
146
+ # @api private
147
+ def known_application_names
148
+ Dir.glob(applications_path.join('/*')).map do |name|
149
+ File.basename(name)
150
+ end
151
+ end
152
+
153
+ # @since 0.5.0
154
+ # @api private
155
+ def assert_controller_name!
156
+ if @controller_name.nil? || @controller_name.empty?
157
+ raise ArgumentError.new("Unknown controller, please add controllers name with this syntax controller_name#action_name")
158
+ end
159
+ end
160
+
161
+ # @since 0.5.0
162
+ # @api private
163
+ def assert_action_name!
164
+ if @action_name.nil? || @action_name.strip == ''
165
+ raise ArgumentError.new("Unknown action, please add actions name with this syntax controller_name#action_name")
166
+ end
167
+ end
168
+
169
+ # @since 0.5.0
170
+ # @api private
171
+ def assert_application_name!
172
+ return if !environment.container?
173
+ if @application_name.nil? || @application_name.strip == '' || !application_path.exist?
174
+ existing_apps = known_application_names.join('/')
175
+ raise ArgumentError.new("Unknown app: `#{ @application_name.downcase }'. Please specify one of (#{ existing_apps }) as application name")
176
+ end
177
+ end
178
+
179
+ # @since 0.5.0
180
+ # @api private
181
+ def assert_http_method!
182
+ if !Lotus::Routing::Route::VALID_HTTP_VERBS.include?(http_method.upcase)
183
+ existing_http_methods = Lotus::Routing::Route::VALID_HTTP_VERBS
184
+ raise ArgumentError.new("Unknown HTTP method '#{http_method}', please use one of #{ existing_http_methods.join('/') }.")
185
+ end
186
+ end
187
+
188
+ def routes_path
189
+ if environment.container?
190
+ application_path.join('config', 'routes.rb')
191
+ else
192
+ application_path.join('..', 'config', 'routes.rb')
193
+ end
194
+ end
195
+
196
+ # The directory of the application
197
+ # ./app for 'app' architecture
198
+ # ./apps/APPLICATION_NAME for 'container'
199
+ def application_path
200
+ if environment.container?
201
+ applications_path.join(application_name_as_snake_case)
202
+ else
203
+ Pathname.new('app')
204
+ end
205
+ end
206
+
207
+ # The parent dir of the application directory.
208
+ def applications_path
209
+ Pathname.new('apps')
210
+ end
211
+
212
+ def view_path
213
+ application_path.join('views', @controller_pathname, "#{@action_name.downcase}.rb")
214
+ end
215
+
216
+ def view_spec_path
217
+ spec_root.join('views', @controller_pathname, "#{@action_name.downcase}_spec.rb")
218
+ end
219
+
220
+ def template_path
221
+ application_path.join('templates', @controller_pathname, "#{@action_name.downcase}.html.#{template_engine}")
222
+ end
223
+
224
+ def action_path
225
+ application_path.join('controllers', @controller_pathname, "#{@action_name.downcase}.rb")
226
+ end
227
+
228
+ def action_spec_path
229
+ spec_root.join('controllers', @controller_pathname, "#{ @action_name.downcase}_spec.rb")
230
+ end
231
+
232
+ def spec_root
233
+ Pathname.new('spec').join(app_base_dir)
234
+ end
235
+
236
+ def relative_action_path
237
+ path = Pathname.new('.').join('..', '..', '..')
238
+ path = path.join('..') if environment.container?
239
+ path.join(application_path, 'controllers', @controller_pathname, @action_name.downcase)
240
+ end
241
+
242
+ def relative_view_path
243
+ path = Pathname.new('.').join('..', '..', '..')
244
+ path = path.join('..') if environment.container?
245
+ path.join(application_path, 'views', @controller_pathname, @action_name.downcase)
246
+ end
247
+
248
+ def app_base_dir
249
+ if environment.container?
250
+ application_name_as_snake_case
251
+ else
252
+ ''
253
+ end
254
+ end
255
+
256
+ def application_name_as_snake_case
257
+ @application_name.gsub(/(.)([A-Z])/,'\1_\2').downcase
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,116 @@
1
+ require 'lotus/commands/generate/abstract'
2
+ require 'lotus/application_name'
3
+ require 'securerandom'
4
+
5
+ module Lotus
6
+ module Commands
7
+ class Generate
8
+ class App < Abstract
9
+
10
+ attr_reader :base_path
11
+
12
+ def initialize(options, application_name)
13
+ super(options)
14
+
15
+ assert_application_name!(application_name)
16
+ assert_architecture!
17
+
18
+ @application_name = ApplicationName.new(application_name)
19
+ @base_path = Pathname.pwd
20
+ end
21
+
22
+ def map_templates
23
+ add_mapping('application.rb.tt', 'application.rb')
24
+ add_mapping('config/routes.rb.tt', 'config/routes.rb')
25
+ add_mapping('views/application_layout.rb.tt', 'views/application_layout.rb')
26
+ add_mapping('templates/application.html.erb.tt', 'templates/application.html.erb')
27
+ add_mapping('favicon.ico', 'assets/favicon.ico')
28
+
29
+ add_mapping('.gitkeep', 'controllers/.gitkeep')
30
+ add_mapping('.gitkeep', 'assets/images/.gitkeep')
31
+ add_mapping('.gitkeep', 'assets/javascripts/.gitkeep')
32
+ add_mapping('.gitkeep', 'assets/stylesheets/.gitkeep')
33
+ add_mapping('.gitkeep', "../../spec/#{ app_name }/features/.gitkeep")
34
+ add_mapping('.gitkeep', "../../spec/#{ app_name }/controllers/.gitkeep")
35
+ add_mapping('.gitkeep', "../../spec/#{ app_name }/views/.gitkeep")
36
+ end
37
+
38
+ def template_options
39
+ {
40
+ app_name: app_name,
41
+ upcase_app_name: upcase_app_name,
42
+ classified_app_name: classified_app_name,
43
+ app_base_url: application_base_url,
44
+ app_base_path: application_base_path,
45
+ }
46
+ end
47
+
48
+ def post_process_templates
49
+ add_require_app
50
+ add_mount_app
51
+ add_web_session_secret
52
+ end
53
+
54
+ private
55
+
56
+ def application_base_url
57
+ options.fetch(:application_base_url, "/#{app_name}")
58
+ end
59
+
60
+ def add_require_app
61
+ # Add "require_relative '../apps/web/application'"
62
+ generator.inject_into_file base_path.join('config/environment.rb'), after: /require_relative '\.\.\/lib\/(.*)'/ do
63
+ "\nrequire_relative '../apps/#{ app_name }/application'"
64
+ end
65
+ end
66
+
67
+ def add_mount_app
68
+ generator.inject_into_file base_path.join('config/environment.rb'), after: /Lotus::Container.configure do/ do |match|
69
+ "\n mount #{ classified_app_name }::Application, at: '#{ application_base_url }'"
70
+ end
71
+ end
72
+
73
+ def add_web_session_secret
74
+ ['development', 'test'].each do |environment|
75
+ # Add WEB_SESSIONS_SECRET="abc123" (random hex)
76
+ generator.append_to_file base_path.join(".env.#{ environment }") do
77
+ %(#{ upcase_app_name }_SESSIONS_SECRET="#{ SecureRandom.hex(32) }"\n)
78
+ end
79
+ end
80
+ end
81
+
82
+ def target_path
83
+ base_path.join(application_base_path)
84
+ end
85
+
86
+ def app_name
87
+ @application_name.to_s
88
+ end
89
+
90
+ def upcase_app_name
91
+ @application_name.to_env_s
92
+ end
93
+
94
+ def application_base_path
95
+ ["apps", @application_name].join(::File::SEPARATOR)
96
+ end
97
+
98
+ def classified_app_name
99
+ Utils::String.new(app_name).classify
100
+ end
101
+
102
+ def assert_application_name!(value)
103
+ if value.nil? || value.strip.empty?
104
+ raise ArgumentError.new('Application name is nil or empty')
105
+ end
106
+ end
107
+
108
+ def assert_architecture!
109
+ if !environment.container?
110
+ raise ArgumentError.new('App generator is only available for container architecture.')
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end