flame 4.18.1 → 5.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +921 -0
  3. data/LICENSE.txt +19 -0
  4. data/README.md +135 -0
  5. data/lib/flame.rb +12 -4
  6. data/lib/flame/application.rb +93 -40
  7. data/lib/flame/config.rb +73 -0
  8. data/lib/flame/controller.rb +62 -98
  9. data/lib/flame/controller/actions.rb +122 -0
  10. data/lib/flame/controller/cookies.rb +44 -0
  11. data/lib/flame/controller/path_to.rb +63 -0
  12. data/lib/flame/dispatcher.rb +44 -73
  13. data/lib/flame/dispatcher/request.rb +33 -4
  14. data/lib/flame/dispatcher/routes.rb +66 -0
  15. data/lib/flame/dispatcher/static.rb +26 -15
  16. data/lib/flame/errors/argument_not_assigned_error.rb +7 -6
  17. data/lib/flame/errors/config_file_not_found_error.rb +17 -0
  18. data/lib/flame/errors/controller_not_found_error.rb +19 -0
  19. data/lib/flame/errors/route_arguments_order_error.rb +9 -8
  20. data/lib/flame/errors/route_extra_arguments_error.rb +18 -18
  21. data/lib/flame/errors/route_not_found_error.rb +8 -7
  22. data/lib/flame/errors/template_not_found_error.rb +6 -6
  23. data/lib/flame/path.rb +141 -55
  24. data/lib/flame/render.rb +46 -15
  25. data/lib/flame/router.rb +41 -127
  26. data/lib/flame/router/controller_finder.rb +56 -0
  27. data/lib/flame/router/route.rb +16 -54
  28. data/lib/flame/router/routes.rb +136 -0
  29. data/lib/flame/router/routes_refine.rb +144 -0
  30. data/lib/flame/router/routes_refine/mounting.rb +57 -0
  31. data/lib/flame/validators.rb +21 -11
  32. data/lib/flame/version.rb +1 -1
  33. metadata +139 -84
  34. data/bin/flame +0 -71
  35. data/lib/flame/application/config.rb +0 -43
  36. data/lib/flame/dispatcher/cookies.rb +0 -31
  37. data/template/.gitignore +0 -11
  38. data/template/Gemfile +0 -15
  39. data/template/Rakefile.erb +0 -64
  40. data/template/app.rb.erb +0 -7
  41. data/template/config.ru.erb +0 -20
  42. data/template/config/config.rb.erb +0 -14
  43. data/template/config/database.example.yml +0 -5
  44. data/template/config/sequel.rb.erb +0 -15
  45. data/template/config/thin.example.yml +0 -18
  46. data/template/controllers/_base_controller.rb.erb +0 -13
  47. data/template/db/.keep +0 -0
  48. data/template/helpers/.keep +0 -0
  49. data/template/lib/.keep +0 -0
  50. data/template/locales/en.yml +0 -0
  51. data/template/models/.keep +0 -0
  52. data/template/public/.keep +0 -0
  53. data/template/server +0 -49
  54. data/template/views/.keep +0 -0
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015-2017 Alexander Popov (AlexWayfer)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"),
5
+ to deal in the Software without restriction, including without limitation
6
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom
8
+ the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included
11
+ in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
15
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
18
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ <p align="center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/AlexWayfer/flame/master/public/favicon.ico"
4
+ height="150"
5
+ alt="Flame Logo"
6
+ title="Logo from open-source Elusive-Iconfont (https://github.com/reduxframework/elusive-iconfont)"
7
+ />
8
+ </p>
9
+
10
+ <h1 align="center">Flame</h1>
11
+
12
+ <p align="center">
13
+ <a href="https://cirrus-ci.com/github/AlexWayfer/flame/master"><img
14
+ src="https://api.cirrus-ci.com/github/AlexWayfer/flame.svg?branch=master"
15
+ alt="Cirrus CI"
16
+ /></a>
17
+ <a href="https://codecov.io/gh/AlexWayfer/flame"><img
18
+ src="https://img.shields.io/codecov/c/github/AlexWayfer/flame.svg?style=flat-square"
19
+ alt="Codecov"
20
+ /></a>
21
+ <a href="https://codeclimate.com/github/AlexWayfer/flame"><img
22
+ src="https://img.shields.io/codeclimate/maintainability/AlexWayfer/flame.svg?style=flat-square"
23
+ alt="Code Climate"
24
+ /></a>
25
+ <a href="https://depfu.com/repos/AlexWayfer/flame"><img
26
+ src="https://img.shields.io/depfu/AlexWayfer/flame.svg?style=flat-square"
27
+ alt="Depfu"
28
+ /></a>
29
+ <a href="http://inch-ci.org/github/AlexWayfer/flame"><img
30
+ src="http://inch-ci.org/github/AlexWayfer/flame.svg?branch=master&style=flat-square"
31
+ alt="Docs"
32
+ /></a>
33
+ <a href="https://rubygems.org/gems/flame"><img
34
+ src="https://img.shields.io/gem/v/flame.svg?style=flat-square"
35
+ alt="Gem"
36
+ /></a>
37
+ <a href="https://github.com/AlexWayfer/flame/blob/master/LICENSE.txt"><img
38
+ src="https://img.shields.io/github/license/AlexWayfer/flame.svg?style=flat-square"
39
+ alt="MIT license"
40
+ /></a>
41
+ </p>
42
+
43
+ Flame is a small Ruby web framework, built on [Rack](https://github.com/rack/rack),
44
+ inspired by [Gin](https://github.com/0jcasts/gin) (which follows class-controllers style),
45
+ designed as a replacement [Sinatra](https://github.com/sinatra/sinatra)
46
+ or maybe even [Rails](https://github.com/rails/rails).
47
+
48
+ ## Why?
49
+
50
+ I didn't like class methods, especially for controller's hooks — OOP is prettier without it.
51
+ And I found a way to implement controller's hooks without using class methods,
52
+ but with the inheritance (including the including of modules).
53
+ Moreover, with class methods an insufficiently obvious order of hooks (especially with inheritance)
54
+ and complicated implementation of conditions are obtained.
55
+ In this framework everything is Ruby-native as it can be.
56
+
57
+ ## Installation
58
+
59
+ Using the built-in `gem`:
60
+
61
+ ```bash
62
+ $ gem install flame
63
+ ```
64
+
65
+ or with [Bundler](http://bundler.io/):
66
+
67
+ ```ruby
68
+ # Gemfile
69
+ gem 'flame'
70
+ ```
71
+
72
+ ## Usage
73
+
74
+ The simplest example:
75
+
76
+ ```ruby
77
+ # index_controller.rb
78
+
79
+ class IndexController < Flame::Controller
80
+ def index
81
+ view :index # or just `view`, Symbol as method-name by default
82
+ end
83
+
84
+ def hello_world
85
+ "Hello World!"
86
+ end
87
+
88
+ def goodbye
89
+ "Goodbye World!"
90
+ end
91
+ end
92
+
93
+ # app.rb
94
+
95
+ class App < Flame::Application
96
+ mount IndexController do
97
+ # all methods will be mounted automatically, it's just an example of refinement
98
+ get '/hello', :hello_world
99
+ end
100
+ end
101
+
102
+ # config.ru
103
+
104
+ require_relative './index_controller'
105
+
106
+ require_relative './app'
107
+
108
+ run App.new # or `run App`
109
+ ```
110
+
111
+ More at [Wiki](https://github.com/AlexWayfer/flame/wiki).
112
+
113
+ ## Benchmark
114
+
115
+ The last benchmark can be viewed [here](https://github.com/luislavena/bench-micro).
116
+
117
+ ## Development
118
+
119
+ After checking out the repo, run `bundle install` to install dependencies.
120
+
121
+ Then, run `toys rspec` to run the tests.
122
+
123
+ To install this gem onto your local machine, run `toys gem install`.
124
+
125
+ To release a new version, run `toys gem release %version%`.
126
+ See how it works [here](https://github.com/AlexWayfer/gem_toys#release).
127
+
128
+ ## Contributing
129
+
130
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/AlexWayfer/flame).
131
+
132
+ ## License
133
+
134
+ The gem is available as open source under the terms of the
135
+ [MIT License](https://opensource.org/licenses/MIT).
data/lib/flame.rb CHANGED
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack'
3
+ require 'gorilla_patch/inflections'
4
4
 
5
- require_relative 'flame/application'
6
- require_relative 'flame/controller'
7
- require_relative 'flame/version'
5
+ ## Base module
6
+ module Flame
7
+ using GorillaPatch::Inflections
8
+
9
+ %i[Config Application Controller VERSION]
10
+ .each do |constant_name|
11
+ autoload(
12
+ constant_name, "#{__dir__}/flame/#{constant_name.to_s.underscore}"
13
+ )
14
+ end
15
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'application/config'
3
+ require 'addressable'
4
+
4
5
  require_relative 'router'
5
6
  require_relative 'dispatcher'
6
7
 
@@ -8,27 +9,37 @@ module Flame
8
9
  ## Core class, like Framework::Application
9
10
  class Application
10
11
  class << self
11
- attr_accessor :config
12
+ include Memery
13
+
14
+ ## Remember root directory when inherited
15
+ def inherited(app)
16
+ super
17
+ app.root_dir = File.dirname caller(2..2).first.split(':')[0]
18
+ end
19
+
20
+ memoize def config
21
+ Flame::Config.new root_dir
22
+ end
12
23
 
13
24
  ## Router for routing
14
- def router
15
- @router ||= Flame::Router.new(self)
25
+ memoize def router
26
+ Flame::Router.new(self)
16
27
  end
17
28
 
18
- def cached_tilts
19
- @cached_tilts ||= {}
29
+ memoize def cached_tilts
30
+ {}
20
31
  end
21
32
 
22
- ## Generating application config when inherited
23
- def inherited(app)
24
- app.config = Config.new(
25
- app,
26
- default_config_dirs(
27
- root_dir: File.dirname(caller[0].split(':')[0])
28
- ).merge(
29
- environment: ENV['RACK_ENV'] || 'development'
30
- )
31
- )
33
+ ## Require project directories, exclude executable files
34
+ ## @param dirs [Array<String>] Array of directories names
35
+ ## @example Regular require of project
36
+ ## Flame::Application.require_dirs(
37
+ ## %w[config lib models helpers mailers services controllers]
38
+ ## )
39
+ def require_dirs(dirs, ignore: [])
40
+ dirs.each do |dir|
41
+ require_dir File.join(root_dir, dir), ignore: ignore
42
+ end
32
43
  end
33
44
 
34
45
  ## Make available `run Application` without `.new` for `rackup`
@@ -37,53 +48,95 @@ module Flame
37
48
  @app.call env
38
49
  end
39
50
 
51
+ using GorillaPatch::DeepDup
52
+
53
+ ## Build a path to the given controller and action
54
+ ##
55
+ ## @param ctrl [Flame::Controller] class of controller
56
+ ## @param action [Symbol] method of controller
57
+ ## @param args [Hash] parameters for method of controller
58
+ ## @return [String] path for requested method, controller and parameters
59
+ ## @example Path for `show(id)` method of `ArticlesController`
60
+ ## path_to ArticlesController, :show, id: 2 # => "/articles/show/2"
61
+ ## @example Path for `new` method of `ArticlesController` with query
62
+ ## path_to ArticlesController, :new, author_id: 1
63
+ ## # => "/articles/new?author_id=1"
64
+ def path_to(ctrl, action = :index, args = {})
65
+ path = router.path_of(ctrl, action)
66
+
67
+ raise Errors::RouteNotFoundError.new(ctrl, action) unless path
68
+
69
+ args = args.deep_dup
70
+ path = path.assign_arguments(args)
71
+ path = '/' if path.empty?
72
+ query = Rack::Utils.build_nested_query args unless args.empty?
73
+ Addressable::URI.new(path: path, query: query).to_s
74
+ end
75
+
76
+ protected
77
+
78
+ attr_accessor :root_dir
79
+
40
80
  private
41
81
 
82
+ def require_dir(dir, ignore: [])
83
+ files =
84
+ Dir[File.join(dir, '**/*.rb')]
85
+ .reject do |file|
86
+ File.executable?(file) ||
87
+ ignore.any? { |regexp| regexp.match?(file) }
88
+ end
89
+ files.sort_by! do |file|
90
+ [File.basename(file).start_with?('_') ? 1 : 2, file]
91
+ end
92
+ files.each { |file| require File.expand_path(file) }
93
+ end
94
+
42
95
  ## Mount controller in application class
43
- ## @param ctrl [Flame::Controller] the mounted controller class
96
+ ## @param controller [Symbol] the snake-cased name of mounted controller
97
+ ## (without `Controller` or `::IndexController` for namespaces)
44
98
  ## @param path [String, nil] root path for the mounted controller
45
- ## @yield refine defaults pathes for a methods of the mounted controller
99
+ ## @yield refine defaults paths for a methods of the mounted controller
46
100
  ## @example Mount controller with defaults
47
- ## mount ArticlesController
101
+ ## mount :articles # ArticlesController
48
102
  ## @example Mount controller with specific path
49
- ## mount HomeController, '/welcome'
103
+ ## mount :home, '/welcome' # HomeController
50
104
  ## @example Mount controller with specific path of methods
51
- ## mount HomeController do
105
+ ## mount :home do # HomeController
52
106
  ## get '/bye', :goodbye
53
107
  ## post '/greetings', :new
54
108
  ## defaults
55
109
  ## end
56
- def mount(ctrl, path = nil, &block)
57
- router.add_controller(ctrl, path, &block)
110
+ ## @example Mount controller with nested controllers
111
+ ## mount :cabinet do # Cabinet::IndexController
112
+ ## mount :articles # Cabinet::ArticlesController
113
+ ## end
114
+ def mount(controller, path = nil, nested: true, &block)
115
+ ## Add routes from controller to glob array
116
+ router.add Router::RoutesRefine.new(
117
+ namespace_name, controller, path, nested: nested, &block
118
+ )
58
119
  end
59
120
 
60
- ## Initialize default for config directories
61
- def default_config_dirs(root_dir:)
62
- result = { root_dir: File.realpath(root_dir) }
63
- %i[public views config tmp].each do |key|
64
- result[:"#{key}_dir"] = proc { File.join(config[:root_dir], key.to_s) }
121
+ using GorillaPatch::Namespace
122
+
123
+ def namespace_name
124
+ namespace = self
125
+ while namespace.name.nil? && namespace.superclass != Flame::Application
126
+ namespace = superclass
65
127
  end
66
- result
128
+ namespace.deconstantize
67
129
  end
68
130
  end
69
131
 
70
- ## Framework configuration
71
- def config
72
- self.class.config
73
- end
74
-
75
- def router
76
- self.class.router
77
- end
78
-
79
132
  def initialize(app = nil)
80
133
  @app = app
81
134
  end
82
135
 
83
- ## Request recieving method
136
+ ## Request receiving method
84
137
  def call(env)
85
138
  @app.call(env) if @app.respond_to? :call
86
- Flame::Dispatcher.new(self, env).run!
139
+ Flame::Dispatcher.new(self.class, env).run!
87
140
  end
88
141
  end
89
142
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors/config_file_not_found_error'
4
+
5
+ module Flame
6
+ ## Class for application configuration
7
+ class Config < Hash
8
+ DEFAULT_DIRS =
9
+ %i[config log public tmp views].each_with_object({}) do |key, result|
10
+ result[:"#{key}_dir"] = proc { File.join(self[:root_dir], key.to_s) }
11
+ end.freeze
12
+
13
+ ## Create an instance of application config
14
+ ## @param app [Flame::Application] application
15
+ ## @param hash [Hash] config content
16
+ def initialize(root_dir)
17
+ super()
18
+ replace DEFAULT_DIRS.merge(
19
+ root_dir: File.realpath(root_dir),
20
+ environment: ENV['RACK_ENV'] || 'development'
21
+ )
22
+ end
23
+
24
+ ## Get config value by key
25
+ ## @param key [Symbol] config key
26
+ ## @return [Object] config value
27
+ def [](key)
28
+ result = super(key)
29
+ result = instance_exec(&result) if result.class <= Proc && result.parameters.empty?
30
+ result
31
+ end
32
+
33
+ ## Method for loading YAML-files from config directory
34
+ ## @param file [String, Symbol]
35
+ ## file name (typecast to String with '.yaml')
36
+ ## @param key [Symbol, String, nil]
37
+ ## key for allocating YAML in config Hash (typecast to Symbol)
38
+ ## @param set [Boolean] allocating YAML in Config Hash
39
+ ## @param require [Boolean] don't raise an error if file not found and not required
40
+ ## @example Load SMTP file from `config/smtp.yaml' to config[]
41
+ ## config.load_yaml('smtp.yaml')
42
+ ## @example Load SMTP file without extension, by Symbol
43
+ ## config.load_yaml(:smtp)
44
+ ## @example Load SMTP file with other key to config[:mail]
45
+ ## config.load_yaml('smtp.yaml', key: :mail)
46
+ ## @example Load SMTP file without allocating in config[]
47
+ ## config.load_yaml('smtp.yaml', set: false)
48
+ ## @example Try to load nonexistent SMTP file without raising an error
49
+ ## config.load_yaml('smtp.yaml', require: false)
50
+ def load_yaml(file, key: nil, set: true, required: true)
51
+ file = "#{file}.y{a,}ml" if file.is_a? Symbol
52
+
53
+ file_path = find_config_file file, required: required
54
+ return unless file_path
55
+
56
+ yaml = YAML.load_file(file_path)
57
+ key ||= File.basename(file, '.*')
58
+ self[key.to_sym] = yaml if set
59
+ yaml
60
+ end
61
+
62
+ private
63
+
64
+ def find_config_file(filename, required:)
65
+ file_path = Dir[File.join(self[:config_dir], filename)].first
66
+ return file_path if file_path || !required
67
+
68
+ raise Errors::ConfigFileNotFoundError.new(
69
+ filename, self[:config_dir].sub(self[:root_dir], '')
70
+ )
71
+ end
72
+ end
73
+ end
@@ -1,21 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require_relative 'render'
4
+ require 'gorilla_patch/namespace'
5
5
 
6
+ require_relative 'router'
7
+
8
+ require_relative 'controller/actions'
9
+ require_relative 'controller/cookies'
10
+ require_relative 'controller/path_to'
11
+
12
+ ## Just because of `autoload`
6
13
  module Flame
14
+ autoload :Render, "#{__dir__}/render"
15
+
7
16
  ## Class initialize when Dispatcher found route with it
8
17
  ## For new request and response
9
18
  class Controller
10
- extend Forwardable
19
+ extend Actions
20
+ include Memery
11
21
 
12
- FORBIDDEN_ACTIONS = [].freeze
22
+ class << self
23
+ attr_accessor :path_arguments
24
+
25
+ def path
26
+ return self::PATH if const_defined?(:PATH)
27
+
28
+ default_path
29
+ end
13
30
 
14
- ## Shortcut for not-inherited public methods: actions
15
- def self.actions
16
- public_instance_methods(false)
31
+ private
32
+
33
+ using GorillaPatch::Inflections
34
+
35
+ ## Default root path of the controller for requests
36
+ def default_path
37
+ modules = underscore.split('/')
38
+ parts = modules.pop.split('_')
39
+ parts.shift if parts.first == 'index'
40
+ parts.pop if %w[controller ctrl].include? parts.last
41
+ parts = [modules.last] if parts.empty?
42
+ Flame::Path.merge nil, parts.join('_')
43
+ end
17
44
  end
18
45
 
46
+ extend Forwardable
47
+
19
48
  def_delegators(
20
49
  :@dispatcher,
21
50
  :config, :request, :params, :halt, :session, :response, :status, :body,
@@ -23,29 +52,17 @@ module Flame
23
52
  )
24
53
 
25
54
  ## Initialize the controller for request execution
26
- ## @param dispatcher [Flame::Dispatcher] dispatcher object
55
+ ## @param dispatcher [Flame::Dispatcher] host dispatcher
27
56
  def initialize(dispatcher)
28
57
  @dispatcher = dispatcher
29
58
  end
30
59
 
31
- ## Helpers
32
- def path_to(*args)
33
- add_controller_class(args)
34
- @dispatcher.path_to(*args)
60
+ ## Cookies object as Hash
61
+ memoize def cookies
62
+ Cookies.new(request.cookies, response)
35
63
  end
36
64
 
37
- ## Build a URI to the given controller and action, or path
38
- def url_to(*args, **options)
39
- first_arg = args.first
40
- path =
41
- if first_arg.is_a?(String) || first_arg.is_a?(Flame::Path)
42
- static_file = find_static(first_arg)
43
- static_file.path(with_version: options[:version])
44
- else
45
- path_to(*args, **options)
46
- end
47
- "#{request.scheme}://#{request.host_with_port}#{path}"
48
- end
65
+ include Flame::Controller::PathTo
49
66
 
50
67
  ## Redirect for response
51
68
  ## @overload redirect(path, status)
@@ -98,6 +115,7 @@ module Flame
98
115
  content_dis = 'Content-Disposition'
99
116
  response[content_dis] = disposition.to_s
100
117
  return unless filename
118
+
101
119
  response[content_dis] << "; filename=\"#{File.basename(filename)}\""
102
120
  ext = File.extname(filename)
103
121
  response.content_type = ext unless ext.empty?
@@ -107,7 +125,7 @@ module Flame
107
125
  ## @param path [Symbol, nil] path to the template file
108
126
  ## @param options [Hash] options for the `Flame::Render` rendering
109
127
  ## @return [String] rendered template
110
- def view(path = nil, options = {})
128
+ def view(path = nil, options = {}, &block)
111
129
  cache = options.delete(:cache)
112
130
  cache = config[:environment] == 'production' if cache.nil?
113
131
  template = Flame::Render.new(
@@ -115,7 +133,7 @@ module Flame
115
133
  (path || caller_locations(1, 1)[0].label.to_sym),
116
134
  options
117
135
  )
118
- template.render(cache: cache)
136
+ template.render(cache: cache, &block)
119
137
  end
120
138
  alias render view
121
139
 
@@ -127,8 +145,16 @@ module Flame
127
145
  body send(method, *extract_params_for(method))
128
146
  end
129
147
 
148
+ def not_found
149
+ default_body
150
+ end
151
+
130
152
  ## Default method for Internal Server Error, can be inherited
131
- def server_error(_exception)
153
+ ## @param _exception [Exception] exception from code executing
154
+ ## @return [String] content of exception page
155
+ def server_error(exception)
156
+ raise exception if Object.const_defined?(:BetterErrors)
157
+
132
158
  body default_body
133
159
  end
134
160
 
@@ -149,87 +175,25 @@ module Flame
149
175
  body
150
176
  end
151
177
 
178
+ using GorillaPatch::Slice
179
+
180
+ def controller_arguments
181
+ params.slice(*self.class.path_arguments)
182
+ end
183
+
152
184
  def extract_params_for(action)
153
- # Take parameters from action method
185
+ ## Take parameters from action method
154
186
  parameters = method(action).parameters
155
- # Fill variables with values from params
187
+ ## Fill variables with values from params
156
188
  req_values, opt_values = %i[req opt].map! do |type|
157
189
  params.values_at(
158
190
  *parameters.select { |key, _value| key == type }.map!(&:last)
159
191
  )
160
192
  end
161
- # Remove nils from the end of optional values
193
+ ## Remove nils from the end of optional values
162
194
  opt_values.pop while opt_values.last.nil? && !opt_values.empty?
163
- # Concat values
195
+ ## Concat values
164
196
  req_values + opt_values
165
197
  end
166
-
167
- def add_controller_class(args)
168
- args.unshift(self.class) if args[0].is_a?(Symbol)
169
- args.insert(1, :index) if args[0].is_a?(Class) && !args[1].is_a?(Symbol)
170
- end
171
-
172
- class << self
173
- using GorillaPatch::Inflections
174
-
175
- ## Default root path of the controller for requests
176
- def default_path
177
- modules = underscore.split('/')
178
- parts = modules.pop.split('_')
179
- parts.shift if parts.first == 'index'
180
- parts.pop if %w[controller ctrl].include? parts.last
181
- parts = [modules.last] if parts.empty?
182
- Flame::Path.merge nil, parts.join('_')
183
- end
184
-
185
- ## Re-define public instance method from parent
186
- ## @example Inherit controller with parent actions by method
187
- ## class MyController < BaseController.with_actions
188
- ## end
189
- ## @example Define actions from module in controller
190
- ## class MyController < BaseController
191
- ## include with_actions Module1
192
- ## include with_actions Module2
193
- ## ....
194
- ## end
195
- def with_actions(mod = nil)
196
- return mod.extend(ModuleActions) if mod
197
- @with_actions ||= Class.new(self) { extend ParentActions }
198
- end
199
- end
200
-
201
- ## Extension for modules whose public methods will be defined as actions
202
- ## via including
203
- module ModuleActions
204
- def included(ctrl)
205
- public_instance_methods.each do |meth|
206
- ctrl.send :define_method, meth, public_instance_method(meth)
207
- end
208
- end
209
- end
210
-
211
- ## Module for public instance methods re-defining from superclass
212
- ## @example Inherit controller with parent actions without forbidden
213
- ## actions by `extend`
214
- ## class MyController < BaseController
215
- ## FORBIDDEN_ACTIONS = %[foo bar baz].freeze
216
- ## extend Flame::Controller::ParentActions
217
- ## end
218
- module ParentActions
219
- def inherited(ctrl)
220
- ctrl.define_parent_actions
221
- end
222
-
223
- def self.extended(ctrl)
224
- ctrl.define_parent_actions
225
- end
226
-
227
- def define_parent_actions
228
- (superclass.actions - self::FORBIDDEN_ACTIONS).each do |public_method|
229
- um = superclass.public_instance_method(public_method)
230
- define_method public_method, um
231
- end
232
- end
233
- end
234
198
  end
235
199
  end