lotusrb 0.3.2 → 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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/FEATURES.md +17 -0
  4. data/README.md +16 -355
  5. data/lib/lotus.rb +0 -1
  6. data/lib/lotus/action/csrf_protection.rb +167 -0
  7. data/lib/lotus/application.rb +3 -1
  8. data/lib/lotus/cli.rb +14 -13
  9. data/lib/lotus/commands/console.rb +1 -1
  10. data/lib/lotus/commands/db.rb +102 -0
  11. data/lib/lotus/commands/db/abstract.rb +15 -0
  12. data/lib/lotus/commands/db/apply.rb +14 -0
  13. data/lib/lotus/commands/db/console.rb +1 -5
  14. data/lib/lotus/commands/db/create.rb +14 -0
  15. data/lib/lotus/commands/db/drop.rb +14 -0
  16. data/lib/lotus/commands/db/migrate.rb +19 -0
  17. data/lib/lotus/commands/db/prepare.rb +14 -0
  18. data/lib/lotus/commands/db/version.rb +14 -0
  19. data/lib/lotus/commands/generate.rb +20 -20
  20. data/lib/lotus/commands/new.rb +1 -0
  21. data/lib/lotus/commands/routes.rb +1 -2
  22. data/lib/lotus/configuration.rb +29 -0
  23. data/lib/lotus/container.rb +19 -3
  24. data/lib/lotus/environment.rb +62 -9
  25. data/lib/lotus/frameworks.rb +1 -0
  26. data/lib/lotus/generators/action.rb +46 -10
  27. data/lib/lotus/generators/action/action_spec.minitest.tt +1 -1
  28. data/lib/lotus/generators/action/action_spec.rspec.tt +1 -1
  29. data/lib/lotus/generators/action/view_spec.minitest.tt +2 -1
  30. data/lib/lotus/generators/action/view_spec.rspec.tt +2 -1
  31. data/lib/lotus/generators/app.rb +39 -0
  32. data/lib/lotus/generators/app/.gitkeep +1 -0
  33. data/lib/lotus/generators/application/app.rb +184 -0
  34. data/lib/lotus/generators/application/app/.env.development.tt +3 -0
  35. data/lib/lotus/generators/application/app/.env.test.tt +3 -0
  36. data/lib/lotus/generators/application/{container/config → app}/.env.tt +0 -0
  37. data/lib/lotus/generators/application/app/.gitkeep +1 -0
  38. data/lib/lotus/generators/application/app/Gemfile.tt +35 -0
  39. data/lib/lotus/generators/application/app/Rakefile.minitest.tt +10 -0
  40. data/lib/lotus/generators/application/app/Rakefile.rspec.tt +5 -0
  41. data/lib/lotus/generators/application/app/apps/.gitkeep.tt +1 -0
  42. data/lib/lotus/generators/application/app/capybara.rb.rspec.tt +8 -0
  43. data/lib/lotus/generators/application/app/config.ru.tt +3 -0
  44. data/lib/lotus/generators/application/app/config/application.rb.tt +227 -0
  45. data/lib/lotus/generators/application/app/config/environment.rb.tt +5 -0
  46. data/lib/lotus/generators/application/app/config/routes.rb.tt +2 -0
  47. data/lib/lotus/generators/application/app/db/.gitkeep +1 -0
  48. data/lib/lotus/generators/application/app/features_helper.rb.minitest.tt +11 -0
  49. data/lib/lotus/generators/application/app/features_helper.rb.rspec.tt +12 -0
  50. data/lib/lotus/generators/application/app/gitignore.tt +2 -0
  51. data/lib/lotus/generators/application/app/lib/app_name.rb.tt +47 -0
  52. data/lib/lotus/generators/application/app/lib/chirp/entities/.gitkeep +1 -0
  53. data/lib/lotus/generators/application/app/lib/chirp/repositories/.gitkeep +1 -0
  54. data/lib/lotus/generators/application/app/lib/config/mapping.rb.tt +7 -0
  55. data/lib/lotus/generators/application/app/lotusrc.tt +3 -0
  56. data/lib/lotus/generators/application/app/rspec.rspec.tt +2 -0
  57. data/lib/lotus/generators/application/app/schema.sql.tt +0 -0
  58. data/lib/lotus/generators/application/app/spec_helper.rb.minitest.tt +7 -0
  59. data/lib/lotus/generators/application/app/spec_helper.rb.rspec.tt +100 -0
  60. data/lib/lotus/generators/application/app/templates/application.html.erb.tt +9 -0
  61. data/lib/lotus/generators/application/app/views/application_layout.rb.tt +7 -0
  62. data/lib/lotus/generators/application/container.rb +37 -13
  63. data/lib/lotus/generators/application/container/{config/.env.development.tt → .env.development.tt} +0 -0
  64. data/lib/lotus/generators/application/container/{config/.env.test.tt → .env.test.tt} +0 -0
  65. data/lib/lotus/generators/application/container/.env.tt +1 -0
  66. data/lib/lotus/generators/application/container/lib/app_name.rb.tt +9 -0
  67. data/lib/lotus/generators/application/container/schema.sql.tt +0 -0
  68. data/lib/lotus/generators/migration.rb +58 -0
  69. data/lib/lotus/generators/migration/migration.rb.tt +4 -0
  70. data/lib/lotus/generators/model.rb +10 -7
  71. data/lib/lotus/generators/slice.rb +4 -12
  72. data/lib/lotus/generators/slice/application.rb.tt +3 -19
  73. data/lib/lotus/generators/slice/config/routes.rb.tt +1 -7
  74. data/lib/lotus/loader.rb +15 -1
  75. data/lib/lotus/lotusrc.rb +8 -3
  76. data/lib/lotus/templates/{welcome.html → welcome.html.erb} +4 -3
  77. data/lib/lotus/version.rb +1 -1
  78. data/lib/lotus/welcome.rb +20 -1
  79. data/lotusrb.gemspec +5 -5
  80. metadata +67 -18
  81. data/lib/lotus/generators/slice/action.rb.tt +0 -8
  82. data/lib/lotus/generators/slice/config/mapping.rb.tt +0 -13
  83. data/lib/lotus/generators/slice/templates/template.html.erb.tt +0 -2
  84. data/lib/lotus/generators/slice/view.rb.tt +0 -5
  85. data/lib/lotus/logger.rb +0 -141
data/lib/lotus.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'lotus/version'
2
2
  require 'lotus/application'
3
3
  require 'lotus/container'
4
- require 'lotus/logger'
5
4
  require 'lotus/environment'
6
5
 
7
6
  # A complete web framework for Ruby
@@ -0,0 +1,167 @@
1
+ require 'securerandom'
2
+
3
+ module Lotus
4
+ module Action
5
+ # Invalid CSRF Token
6
+ #
7
+ # @since 0.4.0
8
+ class InvalidCSRFTokenError < ::StandardError
9
+ end
10
+
11
+ # CSRF Protection
12
+ #
13
+ # This security mechanism is enabled automatically if sessions are turned on.
14
+ #
15
+ # It stores a "challenge" token in session. For each "state changing request"
16
+ # (eg. <tt>POST</tt>, <tt>PATCH</tt> etc..), we should send a special param:
17
+ # <tt>_csrf_token</tt>.
18
+ #
19
+ # If the param matches with the challenge token, the flow can continue.
20
+ # Otherwise the application detects an attack attempt, it reset the session
21
+ # and <tt>Lotus::Action::InvalidCSRFTokenError</tt> is raised.
22
+ #
23
+ # We can specify a custom handling strategy, by overriding <tt>#handle_invalid_csrf_token</tt>.
24
+ #
25
+ # Form helper (<tt>#form_for</tt>) automatically sets a hidden field with the
26
+ # correct token. A special view method (<tt>#csrf_token</tt>) is available in
27
+ # case the form markup is manually crafted.
28
+ #
29
+ # We can disable this check on action basis, by overriding <tt>#verify_csrf_token?</tt>.
30
+ #
31
+ # @since 0.4.0
32
+ #
33
+ # @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29
34
+ # @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
35
+ #
36
+ # @example Custom Handling
37
+ # module Web::Controllers::Books
38
+ # class Create
39
+ # include Web::Action
40
+ #
41
+ # def call(params)
42
+ # # ...
43
+ # end
44
+ #
45
+ # private
46
+ #
47
+ # def handle_invalid_csrf_token
48
+ # Web::Logger.warn "CSRF attack: expected #{ session[:_csrf_token] }, was #{ params[:_csrf_token] }"
49
+ # # manual handling
50
+ # end
51
+ # end
52
+ # end
53
+ #
54
+ # @example Bypass Security Check
55
+ # module Web::Controllers::Books
56
+ # class Create
57
+ # include Web::Action
58
+ #
59
+ # def call(params)
60
+ # # ...
61
+ # end
62
+ #
63
+ # private
64
+ #
65
+ # def verify_csrf_token?
66
+ # false
67
+ # end
68
+ # end
69
+ # end
70
+ module CSRFProtection
71
+ # Session and params key for CSRF token.
72
+ #
73
+ # This key is shared with <tt>lotus-controller</tt> and <tt>lotus-helpers</tt>
74
+ #
75
+ # @since 0.4.0
76
+ # @api private
77
+ CSRF_TOKEN = :_csrf_token
78
+
79
+ # Idempotent HTTP methods
80
+ #
81
+ # By default, the check isn't performed if the request method is included
82
+ # in this list.
83
+ #
84
+ # @since 0.4.0
85
+ # @api private
86
+ IDEMPOTENT_HTTP_METHODS = Hash[
87
+ 'GET' => true,
88
+ 'HEAD' => true,
89
+ 'TRACE' => true,
90
+ 'OPTIONS' => true
91
+ ].freeze
92
+
93
+ # @since 0.4.0
94
+ # @api private
95
+ def self.included(action)
96
+ action.class_eval do
97
+ before :set_csrf_token, :verify_csrf_token
98
+ end unless Lotus.env?(:test)
99
+ end
100
+
101
+ private
102
+ # Set CSRF Token in session
103
+ #
104
+ # @since 0.4.0
105
+ # @api private
106
+ def set_csrf_token
107
+ session[CSRF_TOKEN] ||= generate_csrf_token
108
+ end
109
+
110
+ # Verify if CSRF token from params, matches the one stored in session.
111
+ # If not, it raises an error.
112
+ #
113
+ # Don't override this method.
114
+ #
115
+ # To bypass the security check, please override <tt>#verify_csrf_token?</tt>.
116
+ # For custom handling of an attack, please override <tt>#handle_invalid_csrf_token</tt>.
117
+ #
118
+ # @since 0.4.0
119
+ # @api private
120
+ def verify_csrf_token
121
+ handle_invalid_csrf_token if invalid_csrf_token?
122
+ end
123
+
124
+ # Verify if CSRF token from params, matches the one stored in session.
125
+ #
126
+ # Don't override this method.
127
+ #
128
+ # @since 0.4.0
129
+ # @api private
130
+ def invalid_csrf_token?
131
+ verify_csrf_token? &&
132
+ session[CSRF_TOKEN] != params[CSRF_TOKEN]
133
+ end
134
+
135
+ # Generates a random CSRF Token
136
+ #
137
+ # @since 0.4.0
138
+ # @api private
139
+ def generate_csrf_token
140
+ SecureRandom.hex(32)
141
+ end
142
+
143
+ # Decide if perform the check or not.
144
+ #
145
+ # Override and return <tt>false</tt> if you want to bypass security check.
146
+ #
147
+ # @since 0.4.0
148
+ def verify_csrf_token?
149
+ !IDEMPOTENT_HTTP_METHODS[request_method]
150
+ end
151
+
152
+ # Handle CSRF attack.
153
+ #
154
+ # The default policy resets the session and raises an exception.
155
+ #
156
+ # Override this method, for custom handling.
157
+ #
158
+ # @raise [Lotus::Action::InvalidCSRFTokenError]
159
+ #
160
+ # @since 0.4.0
161
+ def handle_invalid_csrf_token
162
+ session.clear
163
+ raise InvalidCSRFTokenError.new
164
+ end
165
+ end
166
+ end
167
+ end
@@ -2,6 +2,7 @@ require 'lotus/utils/class_attribute'
2
2
  require 'lotus/frameworks'
3
3
  require 'lotus/configuration'
4
4
  require 'lotus/loader'
5
+ require 'lotus/logger'
5
6
  require 'lotus/rendering_policy'
6
7
  require 'lotus/middleware'
7
8
 
@@ -105,7 +106,8 @@ module Lotus
105
106
  # @return [Lotus::Application] a new instance of the application
106
107
  #
107
108
  # @since 0.1.0
108
- def initialize
109
+ def initialize(options = {})
110
+ self.class.configuration.path_prefix options[:path_prefix]
109
111
  self.class.load!(self)
110
112
  end
111
113
 
data/lib/lotus/cli.rb CHANGED
@@ -61,14 +61,14 @@ module Lotus
61
61
  end
62
62
 
63
63
  desc 'new', 'generates a new application'
64
- method_option :database, aliases: '-d', desc: 'application database', type: :string, default: 'filesystem'
65
- method_option :architecture, aliases: '-a', desc: 'application architecture', type: :string, default: 'container'
66
- method_option :application, desc: 'application name', type: :string, default: 'web'
67
- method_option :application_base_url, desc: 'application base url', type: :string, default: '/'
68
- method_option :path, desc: 'path', type: :string
69
- method_option :test, desc: 'application test framework (rspec/minitest)', type: :string, default: 'minitest'
70
- method_option :lotus_head, desc: 'use Lotus HEAD', type: :boolean, default: false
71
- method_option :help, aliases: '-h', desc: 'displays the usage method'
64
+ method_option :database, aliases: '-d', desc: 'application database', type: :string, default: 'filesystem'
65
+ method_option :architecture, aliases: ['-a', '--arch'], desc: 'application architecture', type: :string, default: 'container'
66
+ method_option :application, desc: 'application name', type: :string, default: 'web'
67
+ method_option :application_base_url, desc: 'application base url', type: :string, default: '/'
68
+ method_option :path, desc: 'path', type: :string
69
+ method_option :test, desc: 'application test framework (rspec/minitest)', type: :string, default: 'minitest'
70
+ method_option :lotus_head, desc: 'use Lotus HEAD', type: :boolean, default: false
71
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
72
72
 
73
73
  def new(name = nil)
74
74
  if options[:help] || name.nil?
@@ -79,11 +79,12 @@ module Lotus
79
79
  end
80
80
  end
81
81
 
82
- desc 'generate', 'generates a new action'
83
- method_option :application_base_url, desc: 'application base url', type: :string
84
- method_option :path, desc: 'applications path', type: :string, default: 'apps'
85
- method_option :skip_view, desc: 'skip the creation of view and templates (only for action)', type: :boolean, default: false
86
- method_option :help, aliases: '-h', desc: 'displays the usage method'
82
+ desc 'generate', 'generates action, model or migration'
83
+ method_option :application_base_url, desc: 'application base url', type: :string
84
+ method_option :path, desc: 'applications path', type: :string
85
+ method_option :url, desc: 'relative URL for action', type: :string
86
+ method_option :skip_view, desc: 'skip the creation of view and templates (only for action)', type: :boolean, default: false
87
+ method_option :help, aliases: '-h', desc: 'displays the usage method'
87
88
 
88
89
  # @since 0.3.0
89
90
  # @api private
@@ -24,7 +24,7 @@ module Lotus
24
24
  def start
25
25
  # Clear out ARGV so Pry/IRB don't attempt to parse the rest
26
26
  ARGV.shift until ARGV.empty?
27
- require @environment.env_config.to_s
27
+ @environment.require_application_environment
28
28
 
29
29
  # Add convenience methods to the main:Object binding
30
30
  TOPLEVEL_BINDING.eval('self').send(:include, Methods)
@@ -17,11 +17,113 @@ module Lotus
17
17
  end
18
18
  end
19
19
 
20
+ desc 'db create', 'create database'
21
+
22
+ desc 'create', 'create database for current environment'
23
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
24
+
25
+ def create
26
+ if options[:help]
27
+ invoke :help, ['create']
28
+ else
29
+ assert_allowed_environment!
30
+ require 'lotus/commands/db/create'
31
+ Lotus::Commands::DB::Create.new(environment).start
32
+ end
33
+ end
34
+
35
+ desc 'db drop', 'drop database'
36
+
37
+ desc 'drop', 'drop database for current environment'
38
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
39
+
40
+ def drop
41
+ if options[:help]
42
+ invoke :help, ['drop']
43
+ else
44
+ assert_allowed_environment!
45
+ require 'lotus/commands/db/drop'
46
+ Lotus::Commands::DB::Drop.new(environment).start
47
+ end
48
+ end
49
+
50
+ desc 'db migrate', 'migrate database'
51
+
52
+ desc 'migrate', 'migrate database for current environment'
53
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
54
+
55
+ def migrate(version = nil)
56
+ if options[:help]
57
+ invoke :help, ['migrate']
58
+ else
59
+ require 'lotus/commands/db/migrate'
60
+ Lotus::Commands::DB::Migrate.new(environment, version).start
61
+ end
62
+ end
63
+
64
+ desc 'db apply', 'apply database changes'
65
+
66
+ desc 'apply', 'migrate, dump schema, delete migrations (experimental)'
67
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
68
+
69
+ def apply
70
+ if options[:help]
71
+ invoke :help, ['apply']
72
+ else
73
+ assert_development_environment!
74
+ require 'lotus/commands/db/apply'
75
+ Lotus::Commands::DB::Apply.new(environment).start
76
+ end
77
+ end
78
+
79
+ desc 'db prepare', 'prepare database'
80
+
81
+ desc 'prepare', 'create and migrate database'
82
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
83
+
84
+ def prepare
85
+ if options[:help]
86
+ invoke :help, ['prepare']
87
+ else
88
+ assert_allowed_environment!
89
+ require 'lotus/commands/db/prepare'
90
+ Lotus::Commands::DB::Prepare.new(environment).start
91
+ end
92
+ end
93
+
94
+ desc 'db version', 'database version'
95
+
96
+ desc 'version', 'current database version'
97
+ method_option :environment, desc: 'path to environment configuration (config/environment.rb)'
98
+
99
+ def version
100
+ if options[:help]
101
+ invoke :help, ['version']
102
+ else
103
+ require 'lotus/commands/db/version'
104
+ Lotus::Commands::DB::Version.new(environment).start
105
+ end
106
+ end
107
+
20
108
  private
21
109
 
22
110
  def environment
23
111
  Lotus::Environment.new(options)
24
112
  end
113
+
114
+ def assert_allowed_environment!
115
+ if environment.environment?(:production)
116
+ puts "Can't run this command in production mode"
117
+ exit 1
118
+ end
119
+ end
120
+
121
+ def assert_development_environment!
122
+ unless environment.environment?(:development)
123
+ puts "This command can be ran only in development mode"
124
+ exit 1
125
+ end
126
+ end
25
127
  end
26
128
  end
27
129
  end
@@ -0,0 +1,15 @@
1
+ module Lotus
2
+ module Commands
3
+ class DB
4
+ class Abstract
5
+ def initialize(environment)
6
+ environment.require_application_environment
7
+ end
8
+
9
+ def start
10
+ raise NotImplementedError
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Apply < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.apply
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -10,7 +10,7 @@ module Lotus
10
10
  @name = name
11
11
  @environment = environment
12
12
  @env_options = environment.to_options
13
- load_config
13
+ @environment.require_application_environment
14
14
  end
15
15
 
16
16
  def start
@@ -44,10 +44,6 @@ module Lotus
44
44
  def connection_string
45
45
  adapter_class.new(mapper, adapter_config.uri).connection_string
46
46
  end
47
-
48
- def load_config
49
- require @env_options[:env_config]
50
- end
51
47
  end
52
48
  end
53
49
  end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Create < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.create
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Drop < Abstract
7
+ def start
8
+ require 'lotus/model/migrator'
9
+ Lotus::Model::Migrator.drop
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'lotus/commands/db/abstract'
2
+
3
+ module Lotus
4
+ module Commands
5
+ class DB
6
+ class Migrate < Abstract
7
+ def initialize(environment, version)
8
+ super(environment)
9
+ @version = version
10
+ end
11
+
12
+ def start
13
+ require 'lotus/model/migrator'
14
+ Lotus::Model::Migrator.migrate(version: @version)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end