pakyow-core 0.10.2 → 0.11.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 (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