pakyow-core 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/pakyow-core/CHANGELOG.md +23 -0
  3. data/pakyow-core/lib/pakyow/core/app.rb +81 -0
  4. data/pakyow-core/lib/{core → pakyow/core}/app_context.rb +0 -0
  5. data/pakyow-core/lib/pakyow/core/base.rb +61 -0
  6. data/pakyow-core/lib/pakyow/core/call_context.rb +171 -0
  7. data/pakyow-core/lib/{core → pakyow/core}/config/app.rb +10 -30
  8. data/pakyow-core/lib/pakyow/core/config/cookies.rb +4 -0
  9. data/pakyow-core/lib/{core → pakyow/core}/config/logger.rb +11 -16
  10. data/pakyow-core/lib/pakyow/core/config/reloader.rb +10 -0
  11. data/pakyow-core/lib/{core → pakyow/core}/config/server.rb +2 -4
  12. data/pakyow-core/lib/pakyow/core/config/session.rb +41 -0
  13. data/pakyow-core/lib/{core → pakyow/core}/config.rb +2 -0
  14. data/pakyow-core/lib/{core → pakyow/core}/errors.rb +0 -0
  15. data/pakyow-core/lib/pakyow/core/helpers/configuring.rb +142 -0
  16. data/pakyow-core/lib/pakyow/core/helpers/hooks.rb +106 -0
  17. data/pakyow-core/lib/pakyow/core/helpers/running.rb +124 -0
  18. data/pakyow-core/lib/{core → pakyow/core}/helpers.rb +21 -5
  19. data/pakyow-core/lib/{core → pakyow/core}/loader.rb +1 -1
  20. data/pakyow-core/lib/{core → pakyow/core}/middleware/logger.rb +8 -2
  21. data/pakyow-core/lib/pakyow/core/middleware/override.rb +3 -0
  22. data/pakyow-core/lib/pakyow/core/middleware/reloader.rb +23 -0
  23. data/pakyow-core/lib/pakyow/core/middleware/req_path_normalizer.rb +49 -0
  24. data/pakyow-core/lib/pakyow/core/middleware/session.rb +5 -0
  25. data/pakyow-core/lib/pakyow/core/middleware/static.rb +76 -0
  26. data/pakyow-core/lib/{core → pakyow/core}/multilog.rb +0 -0
  27. data/pakyow-core/lib/{core → pakyow/core}/request.rb +7 -3
  28. data/pakyow-core/lib/{core → pakyow/core}/response.rb +4 -2
  29. data/pakyow-core/lib/{core → pakyow/core}/route_eval.rb +0 -0
  30. data/pakyow-core/lib/{core → pakyow/core}/route_expansion_eval.rb +1 -1
  31. data/pakyow-core/lib/{core → pakyow/core}/route_lookup.rb +0 -0
  32. data/pakyow-core/lib/{core → pakyow/core}/route_merger.rb +0 -0
  33. data/pakyow-core/lib/{core → pakyow/core}/route_module.rb +0 -0
  34. data/pakyow-core/lib/{core → pakyow/core}/route_set.rb +2 -2
  35. data/pakyow-core/lib/{core → pakyow/core}/route_template_defaults.rb +0 -0
  36. data/pakyow-core/lib/{core → pakyow/core}/route_template_eval.rb +0 -0
  37. data/pakyow-core/lib/{core → pakyow/core}/router.rb +4 -0
  38. data/pakyow-core/lib/pakyow/core.rb +8 -0
  39. data/pakyow-core/lib/pakyow-core.rb +1 -11
  40. metadata +41 -34
  41. data/pakyow-core/lib/core/app.rb +0 -469
  42. data/pakyow-core/lib/core/base.rb +0 -56
  43. data/pakyow-core/lib/core/config/cookies.rb +0 -4
  44. data/pakyow-core/lib/core/middleware/reloader.rb +0 -14
  45. data/pakyow-core/lib/core/middleware/static.rb +0 -40
  46. data/pakyow-core/lib/views/errors/404.html +0 -13
  47. data/pakyow-core/lib/views/errors/500.html +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50ae18ead2f40115c95fd5d0cd06c1d33168aef5
4
- data.tar.gz: ba3af0e47bd3f344b0a3d6f0c694dda4ac7d2cfb
3
+ metadata.gz: 7d5b282f6dbfaf9f1aa89fb990e07d0eaf725bd5
4
+ data.tar.gz: 7b8acceb89ef30e66f1af2905f817b68ec4485ce
5
5
  SHA512:
6
- metadata.gz: 99379ff43be348f4ebae46c630c8592f718b4b4659f3fd0bbc486f23182b968b3de9af1e17b81d899ccfb0e006c48e4efe01c82291a786e4443c9d2a626b8210
7
- data.tar.gz: 72be27310400fb3da72d759efd71fc3ff5f926b01475df112e8d6b2af19ddf767d9940658fe9c277dce24ed935f7e9a410838ecbaf586cdcf4f92400b36e2564
6
+ metadata.gz: bfe95bcda07690cfa0aec13df8a9be8806564c5e6464a4786418147fda398aad722ab644a4c585b6dc68c41ff9ff8ee40b141065b5fe1b69fd58933c8ed80d9f
7
+ data.tar.gz: 16ec3ccb608ffddde3f6681d7a2461eb47a7a119078ea931e886e12889088869d0612a4a4d368dbcf27aa7c307b73c561497396fe1e62f297c807a69f61e7ed7
@@ -1,3 +1,26 @@
1
+ # 0.11.0
2
+
3
+ * Adds a `configure` hook for evaluating code before/after configuring app
4
+ * The `Pakyow::App.define` method now returns the defined app
5
+ * Explicitly requires `pakyow-support` so core works on its own
6
+ * Adds a convenience method for defining a restful resource
7
+ * Adds a `reload` hook for evaluating code before/after reloading app
8
+ * Better support for sending any IO object or String data
9
+ * Defaults to `global` when defining app configuration
10
+ * Adds a new `reloader` config namespace
11
+ * Redirects requests with `//` to single-slash path
12
+ * Adds the ability to easily disable middleware
13
+ * Bundles and configures middleware for session handling
14
+ * Automatically removes the trailing `/` in request path
15
+ * Now loads the global config options before env-specific options
16
+ * Adds the source directory to the load path
17
+ * Sets default Content-Type to utf-8
18
+ * Moves everything into the Pakyow namespace
19
+ * Changes to allow error hooks to halt execution
20
+ * Adds a convenience method for checking the existence of a route
21
+ * Protects against internal failures when calling a route with an unknown method
22
+ * Fixes route hook execution order, prioritizing hooks defined in outer scopes
23
+
1
24
  # 0.10.2 / 2015-11-15
2
25
 
3
26
  * Fixes issues presenting error views from gem
@@ -0,0 +1,81 @@
1
+ require_relative 'helpers/configuring'
2
+ require_relative 'helpers/running'
3
+ require_relative 'helpers/hooks'
4
+
5
+ require_relative 'call_context'
6
+ require_relative 'helpers'
7
+ require_relative 'config'
8
+ require_relative 'loader'
9
+ require_relative 'router'
10
+
11
+ module Pakyow
12
+ # The main app object.
13
+ #
14
+ # @api public
15
+ class App
16
+ extend Helpers::Hooks
17
+ extend Helpers::Configuring
18
+ extend Helpers::Running
19
+
20
+ class << self
21
+ # Convenience method for accessing app configuration object.
22
+ #
23
+ # @api public
24
+ def config
25
+ Pakyow::Config
26
+ end
27
+
28
+ # Resets app state.
29
+ #
30
+ # @api private
31
+ def reset
32
+ instance_variables.each do |ivar|
33
+ remove_instance_variable(ivar)
34
+ end
35
+ end
36
+ end
37
+
38
+ include Helpers::App
39
+
40
+ def initialize
41
+ Pakyow.app = self
42
+
43
+ @loader = Loader.new
44
+
45
+ hook_around :init do
46
+ load_app
47
+ end
48
+ end
49
+
50
+ # @api private
51
+ def call(env)
52
+ CallContext.new(env).process.finish
53
+ end
54
+
55
+ # Reloads the app.
56
+ #
57
+ # @api private
58
+ def reload
59
+ hook_around :reload do
60
+ load_app
61
+ end
62
+ end
63
+
64
+ protected
65
+
66
+ def load_app
67
+ hook_around :load do
68
+ @loader.load_from_path(Pakyow::Config.app.src_dir)
69
+ load_routes unless Pakyow::Config.app.ignore_routes
70
+ end
71
+ end
72
+
73
+ def load_routes
74
+ Router.instance.reset
75
+
76
+ self.class.routes.each_pair do |set_name, block|
77
+ Router.instance.set(set_name, &block)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,61 @@
1
+ require 'pakyow/core/helpers'
2
+ require 'pakyow/core/multilog'
3
+ require 'pakyow/core/app_context'
4
+ require 'pakyow/core/request'
5
+ require 'pakyow/core/response'
6
+ require 'pakyow/core/loader'
7
+ require 'pakyow/core/router'
8
+ require 'pakyow/core/route_merger'
9
+ require 'pakyow/core/route_module'
10
+ require 'pakyow/core/route_set'
11
+ require 'pakyow/core/route_eval'
12
+ require 'pakyow/core/route_expansion_eval'
13
+ require 'pakyow/core/route_template_eval'
14
+ require 'pakyow/core/route_template_defaults'
15
+ require 'pakyow/core/route_lookup'
16
+ require 'pakyow/core/app'
17
+ require 'pakyow/core/errors'
18
+
19
+ require 'pakyow/core/config'
20
+ require 'pakyow/core/config/reloader'
21
+ require 'pakyow/core/config/app'
22
+ require 'pakyow/core/config/server'
23
+ require 'pakyow/core/config/cookies'
24
+ require 'pakyow/core/config/logger'
25
+ require 'pakyow/core/config/session'
26
+
27
+ require 'pakyow/core/middleware/override'
28
+ require 'pakyow/core/middleware/reloader'
29
+ require 'pakyow/core/middleware/req_path_normalizer'
30
+ require 'pakyow/core/middleware/session'
31
+ require 'pakyow/core/middleware/static'
32
+ require 'pakyow/core/middleware/logger'
33
+
34
+ module Pakyow
35
+ class << self
36
+ attr_accessor :app, :logger
37
+ end
38
+
39
+ def self.configure_logger
40
+ logs = []
41
+
42
+ if File.directory?(Config.logger.path)
43
+ log_path = File.join(Config.logger.path, Config.logger.filename)
44
+
45
+ begin
46
+ log = File.open(log_path, 'a')
47
+ log.sync if Config.logger.sync
48
+
49
+ logs << log
50
+ rescue StandardError => e
51
+ warn "Error opening '#{log_path}' for writing"
52
+ end
53
+ end
54
+
55
+ logs << $stdout if Config.logger.stdout
56
+
57
+ io = logs.count > 1 ? MultiLog.new(*logs) : logs[0]
58
+
59
+ Pakyow.logger = Logger.new(io, Config.logger.level, Config.logger.colorize, Config.logger.auto_flush)
60
+ end
61
+ end
@@ -0,0 +1,171 @@
1
+ require 'pakyow/core/helpers'
2
+ require 'pakyow/core/helpers/hooks'
3
+ require 'pakyow/core/router'
4
+ require 'pakyow/core/request'
5
+ require 'pakyow/core/response'
6
+ require 'pakyow/core/app_context'
7
+
8
+ module Pakyow
9
+ class CallContext
10
+ include Helpers
11
+ include Helpers::Context
12
+ include Helpers::Hooks::InstanceMethods
13
+
14
+ attr_writer :context
15
+
16
+ def initialize(env)
17
+ @router = Pakyow::Router.instance
18
+
19
+ req = Request.new(env)
20
+ res = Response.new
21
+
22
+ # set response format based on request
23
+ res.format = req.format
24
+
25
+ @context = AppContext.new(req, res)
26
+ end
27
+
28
+ def process
29
+ hook_around :process do
30
+ set_initial_cookies
31
+
32
+ @found = false
33
+ catch :halt do
34
+ hook_around :route do
35
+ @found = @router.perform(context, self) {
36
+ call_hooks :after, :match
37
+ }
38
+ end
39
+
40
+ handle(404, false) unless found?
41
+ end
42
+
43
+ set_cookies
44
+ end
45
+
46
+ self
47
+ rescue StandardError => error
48
+ request.error = error
49
+
50
+ catch :halt do
51
+ hook_around :error do
52
+ handle(500, false) unless found?
53
+ end
54
+ end
55
+
56
+ self
57
+ end
58
+
59
+ # Interrupts the application and returns response immediately.
60
+ #
61
+ def halt
62
+ throw :halt, response
63
+ end
64
+
65
+ # Routes the request to different logic.
66
+ #
67
+ def reroute(location, method = nil)
68
+ location = @router.path(location)
69
+ request.setup(location, method)
70
+
71
+ call_hooks :before, :route
72
+ call_hooks :after, :match
73
+ @router.reroute(request)
74
+ call_hooks :after, :route
75
+ end
76
+
77
+ # Sends data in the response (immediately). Accepts a string of data or a File,
78
+ # mime-type (auto-detected; defaults to octet-stream), and optional file name.
79
+ #
80
+ # If a File, mime type will be guessed. Otherwise mime type and file name will
81
+ # default to whatever is set in the response.
82
+ #
83
+ def send(file_or_data, type = nil, send_as = nil)
84
+ if file_or_data.is_a?(IO)
85
+ data = file_or_data
86
+
87
+ if file_or_data.is_a?(File)
88
+ # auto set type based on file type
89
+ type ||= Rack::Mime.mime_type("." + String.split_at_last_dot(file_or_data.path)[1])
90
+ end
91
+ elsif file_or_data.is_a?(String)
92
+ data = StringIO.new(file_or_data)
93
+ else
94
+ raise ArgumentError, 'Expected an IO or String object'
95
+ end
96
+
97
+ headers = {}
98
+ headers["Content-Type"] = type if type
99
+ headers["Content-disposition"] = "attachment; filename=#{send_as}" if send_as
100
+
101
+ res.body = data
102
+ res.header.merge!(headers)
103
+
104
+ halt
105
+ end
106
+
107
+ # Redirects to location (immediately).
108
+ #
109
+ def redirect(location, status_code = 302)
110
+ location = @router.path(location)
111
+
112
+ headers = response ? response.header : {}
113
+ headers = headers.merge({'Location' => location})
114
+
115
+ self.context.response = Response.new('', status_code, headers)
116
+ halt
117
+ end
118
+
119
+ def handle(name_or_code, from_logic = true)
120
+ hook_around :route do
121
+ @router.handle(name_or_code, self, from_logic)
122
+ end
123
+ end
124
+
125
+ def finish
126
+ res.finish
127
+ end
128
+
129
+ protected
130
+
131
+ def found?
132
+ @found
133
+ end
134
+
135
+ def call_hooks(type, trigger)
136
+ Pakyow::App.hook(type, trigger).each do |block|
137
+ instance_exec(&block)
138
+ end
139
+ end
140
+
141
+ def set_cookies
142
+ request.cookies.each_pair {|k, v|
143
+ response.delete_cookie(k) if v.nil?
144
+
145
+ # cookie is already set with value, ignore
146
+ next if @initial_cookies.include?(k.to_s) && @initial_cookies[k.to_s] == v
147
+
148
+ # set cookie with defaults
149
+ response.set_cookie(k, {
150
+ :path => config.cookies.path,
151
+ :expires => config.cookies.expiration,
152
+ :value => v
153
+ })
154
+ }
155
+
156
+ # delete cookies that are no longer present
157
+ @initial_cookies.each {|k|
158
+ response.delete_cookie(k) unless request.cookies.key?(k.to_s)
159
+ }
160
+ end
161
+
162
+ # Stores set cookies at beginning of request cycle
163
+ # for comparison at the end of the cycle
164
+ def set_initial_cookies
165
+ @initial_cookies = {}
166
+ request.cookies.each {|k,v|
167
+ @initial_cookies[k] = v
168
+ }
169
+ end
170
+ end
171
+ end
@@ -1,7 +1,6 @@
1
- Pakyow::Config.register(:app) { |config|
1
+ Pakyow::Config.register :app do |config|
2
2
 
3
- # if true, the app will be reloaded on every request
4
- config.opt :auto_reload
3
+ config.opt :name, 'pakyow'
5
4
 
6
5
  # if true, errors are displayed in the browser
7
6
  config.opt :errors_in_browser
@@ -10,14 +9,14 @@ Pakyow::Config.register(:app) { |config|
10
9
  config.opt :root, File.dirname('')
11
10
 
12
11
  # the location of the app's resources
13
- config.opt :resources, lambda {
12
+ config.opt :resources, -> {
14
13
  @resources ||= {
15
14
  default: File.join(root, 'public')
16
15
  }
17
16
  }
18
17
 
19
18
  # the location of the app's source code
20
- config.opt :src_dir, lambda { File.join(root, 'app', 'lib') }
19
+ config.opt :src_dir, -> { File.join(root, 'app', 'lib') }
21
20
 
22
21
  # the environment to run in, if one isn't provided
23
22
  config.opt :default_environment, :development
@@ -31,39 +30,20 @@ Pakyow::Config.register(:app) { |config|
31
30
  # if true, views are visible without a route defined
32
31
  config.opt :all_views_visible, true
33
32
 
34
- # whether or not pakyow should log to stdout
35
- config.opt :log_output, true
36
-
37
- # whether or not pakyow should write to a log
38
- config.opt :log, true
39
-
40
33
  # whether or not pakyow should serve static files
41
34
  config.opt :static, true
42
35
 
43
36
  # stores the path to the app definition
44
- config.opt :path, lambda { Pakyow::App.path }
37
+ config.opt :path, -> { Pakyow::App.path }
45
38
 
46
39
  # stores the envs an app is run in
47
40
  config.opt :loaded_envs
48
-
49
- }.env(:development) { |opts|
50
-
51
- opts.auto_reload = true
41
+ end.env :development do |opts|
52
42
  opts.errors_in_browser = true
53
43
  opts.static = true
54
-
55
- }.env(:staging) { |opts|
56
-
57
- opts.auto_reload = false
58
- opts.errors_in_browser = true
59
- opts.log_output = false
60
- opts.static = true
61
-
62
- }.env(:production) { |opts|
63
-
64
- opts.auto_reload = false
44
+ end.env :production do |opts|
65
45
  opts.errors_in_browser = false
66
- opts.log_output = false
67
46
  opts.static = true
68
-
69
- }
47
+ end.env :prototype do |opts|
48
+ opts.ignore_routes = false
49
+ end
@@ -0,0 +1,4 @@
1
+ Pakyow::Config.register :cookies do |config|
2
+ config.opt :path, '/'
3
+ config.opt :expiration, -> { Time.now + 60 * 60 * 24 * 7 }
4
+ end
@@ -1,10 +1,15 @@
1
- Pakyow::Config.register(:logger) { |config|
1
+ Pakyow::Config.register :logger do |config|
2
+ # whether or not pakyow should write to a log
3
+ config.opt :enabled, true
2
4
 
3
5
  # the default level to log at
4
6
  config.opt :level, :debug
5
7
 
8
+ # whether or not pakyow should log to stdout
9
+ config.opt :stdout, true
10
+
6
11
  # where the log file should be placed
7
- config.opt :path, lambda { File.join(Pakyow::Config.app.root, 'log') }
12
+ config.opt :path, -> { File.join(Pakyow::Config.app.root, 'log') }
8
13
 
9
14
  # the name of the log file
10
15
  config.opt :filename, 'pakyow.log'
@@ -17,23 +22,13 @@ Pakyow::Config.register(:logger) { |config|
17
22
 
18
23
  # whether or not the log file should be colorized
19
24
  config.opt :colorize
20
-
21
- }.env(:development) { |opts|
22
-
25
+ end.env :development do |opts|
23
26
  opts.sync = true
24
27
  opts.auto_flush = true
25
28
  opts.colorize = true
26
-
27
- }.env(:staging) { |opts|
28
-
29
+ end.env :production do |opts|
29
30
  opts.sync = false
30
31
  opts.auto_flush = false
32
+ opts.stdout = false
31
33
  opts.colorize = false
32
-
33
- }.env(:production) { |opts|
34
-
35
- opts.sync = false
36
- opts.auto_flush = false
37
- opts.colorize = false
38
-
39
- }
34
+ end
@@ -0,0 +1,10 @@
1
+ Pakyow::Config.register :reloader do |config|
2
+
3
+ # if true, the app will be reloaded on every request
4
+ config.opt :enabled
5
+
6
+ end.env :development do |opts|
7
+ opts.enabled = true
8
+ end.env :production do |opts|
9
+ opts.enabled = false
10
+ end
@@ -1,5 +1,4 @@
1
- Pakyow::Config.register(:server) { |config|
2
-
1
+ Pakyow::Config.register :server do |config|
3
2
  # the port to start `pakyow server`
4
3
  config.opt :port, 3000
5
4
 
@@ -8,5 +7,4 @@ Pakyow::Config.register(:server) { |config|
8
7
 
9
8
  # explicitly set a handler to try (e.g. puma)
10
9
  config.opt :handler
11
-
12
- }
10
+ end
@@ -0,0 +1,41 @@
1
+ Pakyow::Config.register :session do |config|
2
+ # whether or not to use sessions
3
+ config.opt :enabled, true
4
+
5
+ # the session object
6
+ config.opt :object, Rack::Session::Cookie
7
+
8
+ # session middleware config options
9
+ config.opt :options, -> {
10
+ opts = {
11
+ key: Pakyow::Config.session.key,
12
+ secret: Pakyow::Config.session.secret
13
+ }
14
+
15
+ # set optional options if available
16
+ %i(domain path expire_after old_secret).each do |opt|
17
+ value = Pakyow::Config.session.send(opt)
18
+ opts[opt] = value if value
19
+ end
20
+
21
+ opts
22
+ }
23
+
24
+ # the session key
25
+ config.opt :key, -> { "#{Pakyow::Config.app.name}.session" }
26
+
27
+ # the session secret
28
+ config.opt :secret, -> { ENV['SESSION_SECRET'] }
29
+
30
+ # the old session secret (used for rotation)
31
+ config.opt :old_secret
32
+
33
+ # session expiration, in seconds
34
+ config.opt :expire_after
35
+
36
+ # session cookie path
37
+ config.opt :path
38
+
39
+ # session cookie domain
40
+ config.opt :domain
41
+ end
@@ -1,3 +1,5 @@
1
+ require_relative 'errors'
2
+
1
3
  module Pakyow
2
4
  class Config
3
5
  class << self
File without changes
@@ -0,0 +1,142 @@
1
+ module Pakyow
2
+ module Helpers
3
+ # Methods for configuring an app.
4
+ #
5
+ # @api public
6
+ module Configuring
7
+ RESOURCE_ACTIONS = {
8
+ core: Proc.new { |app, set_name, path, block|
9
+ app.routes(set_name) { restful(set_name, path, &block) }
10
+ }
11
+ }
12
+
13
+ module InstanceMethods
14
+ # Convenience method for defining routes on an app instance.
15
+ #
16
+ # @api public
17
+ def routes(set_name = :main, &block)
18
+ self.class.routes(set_name, &block)
19
+ load_routes
20
+ end
21
+
22
+ # Convenience method for defining resources on an app instance.
23
+ #
24
+ # @api public
25
+ def resource(set_name, path, &block)
26
+ self.class.resource(set_name, path, &block)
27
+ end
28
+ end
29
+
30
+ def self.extended(object)
31
+ object.before :reload do
32
+ self.class.send(:load_config)
33
+ end
34
+
35
+ object.send(:include, InstanceMethods)
36
+ end
37
+
38
+ # Absolute path to the file containing the app definition.
39
+ #
40
+ # @api private
41
+ attr_reader :path
42
+
43
+ # Defines an app
44
+ #
45
+ # @api public
46
+ def define(&block)
47
+ raise ArgumentError, 'Expected a block' unless block_given?
48
+
49
+ # Sets the path to the file containiner the app definition for later reloading.
50
+ @path = String.parse_path_from_caller(caller[0])
51
+
52
+ instance_eval(&block)
53
+ self
54
+ end
55
+
56
+ # Defines a route set.
57
+ #
58
+ # @api public
59
+ def routes(set_name = :main, &block)
60
+ return @routes ||= {} unless block_given?
61
+ routes[set_name] = block
62
+ self
63
+ end
64
+
65
+ # Defines a resource.
66
+ #
67
+ # @api public
68
+ def resource(set_name, path, &block)
69
+ raise ArgumentError, 'Expected a block' unless block_given?
70
+
71
+ RESOURCE_ACTIONS.each do |plugin, action|
72
+ action.call(self, set_name, path, block)
73
+ end
74
+ end
75
+
76
+ # Accepts block to be added to middleware stack.
77
+ #
78
+ # @api public
79
+ def middleware(&block)
80
+ return @middleware ||= [] unless block_given?
81
+ middleware << block
82
+ end
83
+
84
+ # Creates an environment.
85
+ #
86
+ # @api public
87
+ def configure(env = :global, &block)
88
+ raise ArgumentError, 'Expected a block' unless block_given?
89
+ env_config[env] = block
90
+ end
91
+
92
+ protected
93
+
94
+ def load_config
95
+ # reload the app file
96
+ load(config.app.path)
97
+
98
+ # reset config
99
+ envs = config.app.loaded_envs
100
+ config.reset
101
+
102
+ # reload config
103
+ load_env_config(*envs)
104
+ end
105
+
106
+ def load_env_config(*env_or_envs)
107
+ hook_around :configure do
108
+ envs = build_envs(env_or_envs)
109
+
110
+ config.app.loaded_envs = envs
111
+ config.env = envs.first
112
+
113
+ load_env(:global)
114
+
115
+ envs.each do |env|
116
+ load_env(env)
117
+ end
118
+
119
+ Pakyow.configure_logger
120
+ end
121
+ end
122
+
123
+ def env_config
124
+ @config ||= {}
125
+ end
126
+
127
+ def build_envs(env_or_envs)
128
+ envs = Array.ensure(env_or_envs)
129
+ envs = (envs.empty? || envs.first.nil?) ? [config.app.default_environment] : envs
130
+ envs.map!(&:to_sym)
131
+ end
132
+
133
+ def load_env(env)
134
+ config.app_config(&config_for(env))
135
+ end
136
+
137
+ def config_for(env)
138
+ env_config.fetch(env) { proc{} }
139
+ end
140
+ end
141
+ end
142
+ end