lotusrb 0.5.0 → 0.6.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 (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