hanami 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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