hanami 0.8.0 → 0.9.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/FEATURES.md +13 -0
  4. data/README.md +28 -1
  5. data/hanami.gemspec +20 -17
  6. data/lib/hanami.rb +106 -7
  7. data/lib/hanami/action/routing_helpers.rb +2 -2
  8. data/lib/hanami/app.rb +72 -0
  9. data/lib/hanami/application.rb +144 -183
  10. data/lib/hanami/application_configuration.rb +1541 -0
  11. data/lib/hanami/application_name.rb +2 -2
  12. data/lib/hanami/application_namespace.rb +12 -0
  13. data/lib/hanami/assets/asset.rb +3 -1
  14. data/lib/hanami/assets/static.rb +3 -9
  15. data/lib/hanami/cli.rb +10 -7
  16. data/lib/hanami/cli_sub_commands/assets.rb +1 -9
  17. data/lib/hanami/cli_sub_commands/generate.rb +16 -0
  18. data/lib/hanami/commands/apps.rb +4 -0
  19. data/lib/hanami/commands/assets/precompile.rb +6 -19
  20. data/lib/hanami/commands/command.rb +64 -0
  21. data/lib/hanami/commands/console.rb +37 -26
  22. data/lib/hanami/commands/db/apply.rb +4 -2
  23. data/lib/hanami/commands/db/console.rb +11 -27
  24. data/lib/hanami/commands/db/create.rb +4 -2
  25. data/lib/hanami/commands/db/drop.rb +4 -2
  26. data/lib/hanami/commands/db/migrate.rb +11 -5
  27. data/lib/hanami/commands/db/prepare.rb +4 -2
  28. data/lib/hanami/commands/db/version.rb +4 -2
  29. data/lib/hanami/commands/generate/abstract.rb +5 -7
  30. data/lib/hanami/commands/generate/action.rb +18 -6
  31. data/lib/hanami/commands/generate/app.rb +15 -2
  32. data/lib/hanami/commands/generate/migration.rb +3 -2
  33. data/lib/hanami/commands/generate/model.rb +4 -3
  34. data/lib/hanami/commands/generate/secret_token.rb +31 -0
  35. data/lib/hanami/commands/new/abstract.rb +14 -5
  36. data/lib/hanami/commands/new/container.rb +1 -0
  37. data/lib/hanami/commands/routes.rb +5 -22
  38. data/lib/hanami/commands/server.rb +14 -142
  39. data/lib/hanami/components.rb +107 -0
  40. data/lib/hanami/components/app/assets.rb +55 -0
  41. data/lib/hanami/components/app/controller.rb +69 -0
  42. data/lib/hanami/components/app/logger.rb +30 -0
  43. data/lib/hanami/components/app/routes.rb +51 -0
  44. data/lib/hanami/components/app/view.rb +40 -0
  45. data/lib/hanami/components/component.rb +166 -0
  46. data/lib/hanami/components/components.rb +366 -0
  47. data/lib/hanami/components/routes_inspector.rb +70 -0
  48. data/lib/hanami/config/load_paths.rb +7 -6
  49. data/lib/hanami/config/mapper.rb +1 -1
  50. data/lib/hanami/config/security.rb +0 -8
  51. data/lib/hanami/configuration.rb +27 -1697
  52. data/lib/hanami/env.rb +67 -0
  53. data/lib/hanami/environment.rb +31 -21
  54. data/lib/hanami/environment_application_configurations.rb +30 -0
  55. data/lib/hanami/frameworks.rb +1 -0
  56. data/lib/hanami/generators/app/application.rb.tt +2 -2
  57. data/lib/hanami/generators/application/app/Gemfile.tt +3 -1
  58. data/lib/hanami/generators/application/app/config/application.rb.tt +2 -2
  59. data/lib/hanami/generators/application/app/gitignore_with_sqlite.tt +3 -0
  60. data/lib/hanami/generators/application/app/lib/app_name.rb.tt +4 -25
  61. data/lib/hanami/generators/application/app/spec_helper.rb.minitest.tt +1 -1
  62. data/lib/hanami/generators/application/app/spec_helper.rb.rspec.tt +1 -1
  63. data/lib/hanami/generators/application/container/Gemfile.tt +3 -1
  64. data/lib/hanami/generators/application/container/capybara.rb.rspec.tt +1 -1
  65. data/lib/hanami/generators/application/container/config.ru.tt +1 -1
  66. data/lib/hanami/generators/application/container/config/environment.rb.tt +35 -1
  67. data/lib/hanami/generators/application/container/features_helper.rb.minitest.tt +1 -1
  68. data/lib/hanami/generators/application/container/gitignore_with_sqlite.tt +3 -0
  69. data/lib/hanami/generators/application/container/lib/project.rb.tt +1 -57
  70. data/lib/hanami/generators/application/container/spec_helper.rb.minitest.tt +1 -1
  71. data/lib/hanami/generators/application/container/spec_helper.rb.rspec.tt +2 -3
  72. data/lib/hanami/generators/database_config.rb +8 -11
  73. data/lib/hanami/generators/model/entity.rb.tt +1 -2
  74. data/lib/hanami/generators/model/repository.rb.tt +1 -2
  75. data/lib/hanami/generators/template_engine.rb +8 -3
  76. data/lib/hanami/generators/test_framework.rb +4 -3
  77. data/lib/hanami/middleware.rb +41 -21
  78. data/lib/hanami/rake_helper.rb +6 -8
  79. data/lib/hanami/server.rb +43 -33
  80. data/lib/hanami/static.rb +2 -2
  81. data/lib/hanami/version.rb +35 -1
  82. data/lib/hanami/welcome.rb +4 -5
  83. metadata +68 -42
  84. data/lib/hanami/commands/db/abstract.rb +0 -19
  85. data/lib/hanami/config/configure.rb +0 -17
  86. data/lib/hanami/config/mapping.rb +0 -12
  87. data/lib/hanami/container.rb +0 -71
  88. data/lib/hanami/generators/application/container/gitignore_with_db.tt +0 -4
  89. data/lib/hanami/loader.rb +0 -257
  90. data/lib/hanami/repositories/car_repository.rb +0 -3
  91. data/lib/hanami/repositories/name_repository.rb +0 -3
@@ -0,0 +1,107 @@
1
+ require 'concurrent'
2
+
3
+ module Hanami
4
+ # Components API
5
+ #
6
+ # Components are an internal Hanami that provides precise loading mechanism
7
+ # for a project. It is responsible to load frameworks, configurations, code, etc..
8
+ #
9
+ # The implementation is thread-safe
10
+ #
11
+ # @since 0.9.0
12
+ # @api private
13
+ module Components
14
+ # Available components
15
+ #
16
+ # @since 0.9.0
17
+ # @api private
18
+ @_components = Concurrent::Hash.new
19
+
20
+ # Resolved components
21
+ #
22
+ # @since 0.9.0
23
+ # @api private
24
+ @_resolved = Concurrent::Map.new
25
+
26
+ # Register a component
27
+ #
28
+ # @param name [String] the unique component name
29
+ # @param blk [Proc] the logic of the component
30
+ #
31
+ # @since 0.9.0
32
+ # @api private
33
+ #
34
+ # @see Hanami::Components::Component
35
+ def self.register(name, &blk)
36
+ @_components[name] = Component.new(name, &blk)
37
+ end
38
+
39
+ # Return a registered component
40
+ #
41
+ # @param name [String] the name of the component
42
+ #
43
+ # @raise [ArgumentError] if the component is unknown
44
+ #
45
+ # @since 0.9.0
46
+ # @api private
47
+ def self.component(name)
48
+ @_components.fetch(name) do
49
+ raise ArgumentError.new("Component not found: `#{name}'.\nAvailable components are: #{@_components.keys.join(', ')}")
50
+ end
51
+ end
52
+
53
+ # Mark a component as resolved by providing a value or a block.
54
+ # In the latter case, the returning value of the block is associated with
55
+ # the component.
56
+ #
57
+ # @param name [String] the name of the component to mark as resolved
58
+ # @param value [Object] the optional value of the component
59
+ # @param blk [Proc] the optional block which returning value is associated with the component.
60
+ #
61
+ # @since 0.9.0
62
+ # @api private
63
+ def self.resolved(name, value = nil, &blk)
64
+ if block_given?
65
+ @_resolved.fetch_or_store(name, &blk)
66
+ else
67
+ @_resolved.compute_if_absent(name) { value }
68
+ end
69
+ end
70
+
71
+ # Ask to resolve a component.
72
+ #
73
+ # This is used as dependency mechanism.
74
+ # For instance `model` component depends on `model.configuration`. Before to
75
+ # resolve `model`, `Components` uses this method to resolve that dependency first.
76
+ #
77
+ # @param names [String,Array<String>] one or more components to be resolved
78
+ #
79
+ # @since 0.9.0
80
+ # @api private
81
+ def self.resolve(*names)
82
+ Array(names).flatten.each do |name|
83
+ @_resolved.fetch_or_store(name) do
84
+ component = @_components.fetch(name)
85
+ component.call(Hanami.configuration)
86
+ end
87
+ end
88
+ end
89
+
90
+ # Return the value of an already resolved component.
91
+ #
92
+ # @param name [String] the component name
93
+ #
94
+ # @raise [ArgumentError] if the component is unknown or not resolved yet.
95
+ #
96
+ # @since 0.9.0
97
+ # @api private
98
+ def self.[](name)
99
+ @_resolved.fetch(name) do
100
+ raise ArgumentError.new("Component not resolved: `#{name}'.\nResolved components are: #{@_resolved.keys.join(', ')}")
101
+ end
102
+ end
103
+
104
+ require 'hanami/components/component'
105
+ require 'hanami/components/components'
106
+ end
107
+ end
@@ -0,0 +1,55 @@
1
+ module Hanami
2
+ module Components
3
+ module App
4
+ # hanami-assets configuration for a sigle Hanami application in the project.
5
+ #
6
+ # @since 0.9.0
7
+ # @api private
8
+ class Assets
9
+ # Configure hanami-assets for a single Hanami application in the project.
10
+ #
11
+ # @param app [Hanami::Configuration::App] a Hanami application
12
+ #
13
+ # @since 0.9.0
14
+ # @api private
15
+ #
16
+ # rubocop:disable Metrics/AbcSize
17
+ # rubocop:disable Metrics/MethodLength
18
+ def self.resolve(app)
19
+ config = app.configuration
20
+ namespace = app.namespace
21
+
22
+ unless namespace.const_defined?('Assets', false)
23
+ assets = Hanami::Assets.duplicate(namespace) do
24
+ root config.root
25
+
26
+ scheme config.scheme
27
+ host config.host
28
+ port config.port
29
+
30
+ public_directory Hanami.public_directory
31
+ prefix Utils::PathPrefix.new('/assets').join(config.path_prefix)
32
+
33
+ manifest Hanami.public_directory.join('assets.json')
34
+ compile true
35
+
36
+ config.assets.__apply(self)
37
+ end
38
+
39
+ assets.configure do
40
+ cdn host != config.host
41
+ end
42
+
43
+ namespace.const_set('Assets', assets)
44
+ end
45
+
46
+ name = "#{app.app_name}.assets"
47
+ Components.resolved(name, namespace.const_get('Assets').configuration)
48
+ Components[name]
49
+ end
50
+ end
51
+ # rubocop:enable Metrics/MethodLength
52
+ # rubocop:enable Metrics/AbcSize
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,69 @@
1
+ require 'hanami/action/session'
2
+ require 'hanami/action/routing_helpers'
3
+
4
+ module Hanami
5
+ module Components
6
+ module App
7
+ # hanami-controller configuration for a sigle Hanami application in the project.
8
+ #
9
+ # @since 0.9.0
10
+ # @api private
11
+ class Controller
12
+ STRICT_TRANSPORT_SECURITY_HEADER = 'Strict-Transport-Security'.freeze
13
+ STRICT_TRANSPORT_SECURITY_DEFAULT_VALUE = 'max-age=31536000'.freeze
14
+
15
+ # Configure hanami-controller for a single Hanami application in the project.
16
+ #
17
+ # @param app [Hanami::Configuration::App] a Hanami application
18
+ #
19
+ # @since 0.9.0
20
+ # @api private
21
+ #
22
+ # rubocop:disable Metrics/AbcSize
23
+ # rubocop:disable Metrics/MethodLength
24
+ def self.resolve(app)
25
+ config = app.configuration
26
+ namespace = app.namespace
27
+
28
+ unless namespace.const_defined?('Controller', false)
29
+ controller = Hanami::Controller.duplicate(namespace) do
30
+ handle_exceptions config.handle_exceptions
31
+ default_request_format config.default_request_format
32
+ default_response_format config.default_response_format
33
+ default_headers(
34
+ Hanami::Config::Security::X_FRAME_OPTIONS_HEADER => config.security.x_frame_options,
35
+ Hanami::Config::Security::X_CONTENT_TYPE_OPTIONS_HEADER => config.security.x_content_type_options,
36
+ Hanami::Config::Security::X_XSS_PROTECTION_HEADER => config.security.x_xss_protection,
37
+ Hanami::Config::Security::CONTENT_SECURITY_POLICY_HEADER => config.security.content_security_policy
38
+ )
39
+ default_headers[STRICT_TRANSPORT_SECURITY_HEADER] = STRICT_TRANSPORT_SECURITY_DEFAULT_VALUE if config.force_ssl
40
+
41
+ if config.cookies.enabled?
42
+ require 'hanami/action/cookies'
43
+ prepare { include Hanami::Action::Cookies }
44
+ cookies config.cookies.default_options
45
+ end
46
+
47
+ if config.sessions.enabled?
48
+ prepare do
49
+ include Hanami::Action::Session
50
+ include Hanami::Action::CSRFProtection
51
+ end
52
+ end
53
+
54
+ prepare { include Hanami::Action::RoutingHelpers }
55
+
56
+ config.controller.__apply(self)
57
+ end
58
+
59
+ namespace.const_set('Controller', controller)
60
+ end
61
+
62
+ Components.resolved "#{app.app_name}.controller", namespace.const_get('Controller').configuration
63
+ end
64
+ end
65
+ # rubocop:enable Metrics/MethodLength
66
+ # rubocop:enable Metrics/AbcSize
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ require 'hanami/logger'
2
+
3
+ module Hanami
4
+ module Components
5
+ module App
6
+ # hanami/logger configuration for a sigle Hanami application in the project.
7
+ #
8
+ # @since 0.9.0
9
+ # @api private
10
+ class Logger
11
+ # Configure hanami/logger for a single Hanami application in the project.
12
+ #
13
+ # @param app [Hanami::Configuration::App] a Hanami application
14
+ #
15
+ # @since 0.9.0
16
+ # @api private
17
+ def self.resolve(app)
18
+ namespace = app.namespace
19
+ return unless namespace.logger.nil?
20
+
21
+ config = app.configuration
22
+
23
+ # TODO: review this logic
24
+ config.logger.app_name(namespace.to_s)
25
+ namespace.logger = config.logger.build
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ require 'hanami/routes'
2
+ require 'hanami/routing/default'
3
+
4
+ module Hanami
5
+ module Components
6
+ module App
7
+ # hanami-router configuration for a sigle Hanami application in the project.
8
+ #
9
+ # @since 0.9.0
10
+ # @api private
11
+ class Routes
12
+ # Configure hanami-router for a single Hanami application in the project.
13
+ #
14
+ # @param app [Hanami::Configuration::App] a Hanami application
15
+ #
16
+ # @since 0.9.0
17
+ # @api private
18
+ def self.resolve(app)
19
+ namespace = app.namespace
20
+ routes = application_routes(app)
21
+
22
+ if namespace.routes.nil? # rubocop:disable Style/IfUnlessModifier
23
+ namespace.routes = Hanami::Routes.new(routes)
24
+ end
25
+
26
+ Components.resolved("#{app.app_name}.routes", routes)
27
+ end
28
+
29
+ def self.application_routes(app) # rubocop:disable Metrics/MethodLength
30
+ config = app.configuration
31
+ namespace = app.namespace
32
+
33
+ resolver = Hanami::Routing::EndpointResolver.new(pattern: config.controller_pattern, namespace: namespace)
34
+ default_app = Hanami::Routing::Default.new
35
+
36
+ Hanami::Router.new(
37
+ resolver: resolver,
38
+ default_app: default_app,
39
+ parsers: config.body_parsers,
40
+ scheme: config.scheme,
41
+ host: config.host,
42
+ port: config.port,
43
+ prefix: config.path_prefix,
44
+ force_ssl: config.force_ssl,
45
+ &config.routes
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,40 @@
1
+ module Hanami
2
+ module Components
3
+ module App
4
+ # hanami-view configuration for a sigle Hanami application in the project.
5
+ #
6
+ # @since 0.9.0
7
+ # @api private
8
+ class View
9
+ # Configure hanami-view for a single Hanami application in the project.
10
+ #
11
+ # @param app [Hanami::Configuration::App] a Hanami application
12
+ #
13
+ # @since 0.9.0
14
+ # @api private
15
+ #
16
+ # rubocop:disable Metrics/AbcSize
17
+ # rubocop:disable Metrics/MethodLength
18
+ def self.resolve(app)
19
+ config = app.configuration
20
+ namespace = app.namespace
21
+
22
+ unless namespace.const_defined?('View', false)
23
+ view = Hanami::View.duplicate(namespace) do
24
+ root config.templates
25
+ layout config.layout
26
+
27
+ config.view.__apply(self)
28
+ end
29
+
30
+ namespace.const_set('View', view)
31
+ end
32
+
33
+ Components.resolved "#{app.app_name}.view", namespace.const_get('View').configuration
34
+ end
35
+ end
36
+ # rubocop:enable Metrics/MethodLength
37
+ # rubocop:enable Metrics/AbcSize
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,166 @@
1
+ module Hanami
2
+ module Components
3
+ # Base component
4
+ #
5
+ # @since 0.9.0
6
+ # @api private
7
+ #
8
+ # @see Hanami::Components
9
+ class Component
10
+ # Instantiate a new component
11
+ #
12
+ # @param name [String] the component name
13
+ # @param blk [Proc] the logic of the component
14
+ #
15
+ # @return [Hanami::Components::Component]
16
+ #
17
+ # @since 0.9.0
18
+ # @api private
19
+ def initialize(name, &blk)
20
+ @name = name
21
+ @requirements = []
22
+ @_prepare = ->(*) {}
23
+ @_resolve = -> {}
24
+ instance_eval(&blk)
25
+ end
26
+
27
+ # Run or resolve the component
28
+ #
29
+ # @param configuration [Hanami::Configuration] the Hanami configuration for the project
30
+ #
31
+ # @since 0.9.0
32
+ # @api private
33
+ def call(configuration)
34
+ resolve_requirements
35
+ _prepare.call(configuration)
36
+
37
+ unless _run.nil?
38
+ _run.call(configuration)
39
+ return
40
+ end
41
+
42
+ resolved(name, _resolve.call(configuration))
43
+ end
44
+
45
+ private
46
+
47
+ # Component name
48
+ #
49
+ # @return [String]
50
+ #
51
+ # @since 0.9.0
52
+ # @api private
53
+ attr_reader :name
54
+
55
+ # Component requirements
56
+ #
57
+ # @return [Array<String>]
58
+ #
59
+ # @since 0.9.0
60
+ # @api private
61
+ attr_reader :requirements
62
+
63
+ # Prepare logic
64
+ #
65
+ # @since 0.9.0
66
+ # @api private
67
+ attr_accessor :_prepare
68
+
69
+ # Resolve logic
70
+ #
71
+ # @since 0.9.0
72
+ # @api private
73
+ attr_accessor :_resolve
74
+
75
+ # Run logic
76
+ #
77
+ # @since 0.9.0
78
+ # @api private
79
+ attr_accessor :_run
80
+
81
+ # Declare component requirement(s)
82
+ #
83
+ # @param components [Array<String>] the name of the other components to
84
+ # depend on
85
+ def requires(*components)
86
+ self.requirements = Array(components).flatten
87
+ end
88
+
89
+ # Declare prepare logic
90
+ #
91
+ # @param blk [Proc] prepare logic
92
+ #
93
+ # @since 0.9.0
94
+ # @api private
95
+ def prepare(&blk)
96
+ self._prepare = blk
97
+ end
98
+
99
+ # Declare resolve logic
100
+ #
101
+ # @param blk [Proc] resolve logic
102
+ #
103
+ # @since 0.9.0
104
+ # @api private
105
+ def resolve(&blk)
106
+ self._resolve = blk
107
+ end
108
+
109
+ # Declare run logic
110
+ #
111
+ # @param blk [Proc] run logic
112
+ #
113
+ # @since 0.9.0
114
+ # @api private
115
+ def run(&blk)
116
+ self._run = blk
117
+ end
118
+
119
+ # Set requirements
120
+ #
121
+ # @param names [Array<String>] the requirements
122
+ #
123
+ # @since 0.9.0
124
+ # @api private
125
+ def requirements=(names)
126
+ @requirements = Array(names).flatten
127
+ end
128
+
129
+ # Resolve the requirements before to execute the logic of this component
130
+ #
131
+ # @since 0.9.0
132
+ # @api private
133
+ #
134
+ # @see Hanami::Components.resolve
135
+ def resolve_requirements
136
+ Components.resolve(requirements)
137
+ end
138
+
139
+ # Get a registered component by name
140
+ #
141
+ # @param name [String] the component name
142
+ #
143
+ # @since 0.9.0
144
+ # @api private
145
+ #
146
+ # @see Hanami::Components.component
147
+ def component(name)
148
+ Components.component(name)
149
+ end
150
+
151
+ # Mark a component as resolved by providing a value or a block.
152
+ #
153
+ # @param name [String] the name of the component to mark as resolved
154
+ # @param value [Object] the optional value of the component
155
+ # @param blk [Proc] the optional block which returning value is associated with the component.
156
+ #
157
+ # @since 0.9.0
158
+ # @api private
159
+ #
160
+ # @see Hanami::Components.resolved
161
+ def resolved(name, value = nil, &blk)
162
+ Components.resolved(name, value, &blk)
163
+ end
164
+ end
165
+ end
166
+ end