flame 5.0.0.rc4 → 5.0.0.rc7
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +929 -0
- data/LICENSE.txt +19 -0
- data/README.md +135 -0
- data/lib/flame/application.rb +47 -46
- data/lib/flame/config.rb +73 -0
- data/lib/flame/controller/actions.rb +122 -0
- data/lib/flame/{dispatcher → controller}/cookies.rb +8 -3
- data/lib/flame/controller/path_to.rb +34 -10
- data/lib/flame/controller.rb +45 -78
- data/lib/flame/dispatcher/request.rb +22 -6
- data/lib/flame/dispatcher/routes.rb +22 -14
- data/lib/flame/dispatcher/static.rb +13 -9
- data/lib/flame/dispatcher.rb +15 -18
- data/lib/flame/errors/argument_not_assigned_error.rb +3 -8
- data/lib/flame/errors/config_file_not_found_error.rb +17 -0
- data/lib/flame/errors/controller_not_found_error.rb +19 -0
- data/lib/flame/errors/route_arguments_order_error.rb +3 -10
- data/lib/flame/errors/route_extra_arguments_error.rb +7 -20
- data/lib/flame/errors/route_not_found_error.rb +4 -9
- data/lib/flame/errors/template_not_found_error.rb +2 -8
- data/lib/flame/path.rb +36 -18
- data/lib/flame/render.rb +13 -5
- data/lib/flame/router/controller_finder.rb +56 -0
- data/lib/flame/router/route.rb +9 -0
- data/lib/flame/router/routes.rb +56 -9
- data/lib/flame/router/routes_refine/mounting.rb +57 -0
- data/lib/flame/router/routes_refine.rb +144 -0
- data/lib/flame/router.rb +7 -157
- data/lib/flame/validators.rb +14 -10
- data/lib/flame/version.rb +1 -1
- data/lib/flame.rb +12 -5
- metadata +107 -96
- data/bin/flame +0 -16
- data/lib/flame/application/config.rb +0 -49
- data/template/.editorconfig +0 -15
- data/template/.gitignore +0 -28
- data/template/.rubocop.yml +0 -14
- data/template/Gemfile +0 -55
- data/template/Rakefile +0 -824
- data/template/application.rb.erb +0 -10
- data/template/config/config.rb.erb +0 -56
- data/template/config/database.example.yml +0 -5
- data/template/config/deploy.example.yml +0 -2
- data/template/config/puma.rb +0 -56
- data/template/config/sequel.rb.erb +0 -22
- data/template/config/server.example.yml +0 -32
- data/template/config/session.example.yml +0 -7
- data/template/config.ru.erb +0 -72
- data/template/controllers/_controller.rb.erb +0 -14
- data/template/controllers/site/_controller.rb.erb +0 -18
- data/template/controllers/site/index_controller.rb.erb +0 -12
- data/template/db/.keep +0 -0
- data/template/filewatchers.yml +0 -12
- data/template/helpers/.keep +0 -0
- data/template/lib/.keep +0 -0
- data/template/locales/en.yml +0 -0
- data/template/models/.keep +0 -0
- data/template/public/.keep +0 -0
- data/template/server +0 -200
- data/template/services/.keep +0 -0
- data/template/views/.keep +0 -0
- data/template/views/site/index.html.erb.erb +0 -1
- data/template/views/site/layout.html.erb.erb +0 -10
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/main/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/main"><img
|
14
|
+
src="https://api.cirrus-ci.com/github/AlexWayfer/flame.svg?branch=main"
|
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=main&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?include_prereleases&style=flat-square"
|
35
|
+
alt="Gem (including prereleases)"
|
36
|
+
/></a>
|
37
|
+
<a href="https://github.com/AlexWayfer/flame/blob/main/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/application.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'addressable'
|
4
|
+
|
4
5
|
require_relative 'router'
|
5
6
|
require_relative 'dispatcher'
|
6
7
|
|
@@ -8,15 +9,25 @@ module Flame
|
|
8
9
|
## Core class, like Framework::Application
|
9
10
|
class Application
|
10
11
|
class << self
|
11
|
-
|
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
|
-
|
25
|
+
memoize def router
|
26
|
+
Flame::Router.new(self)
|
16
27
|
end
|
17
28
|
|
18
|
-
def cached_tilts
|
19
|
-
|
29
|
+
memoize def cached_tilts
|
30
|
+
{}
|
20
31
|
end
|
21
32
|
|
22
33
|
## Require project directories, exclude executable files
|
@@ -25,34 +36,20 @@ module Flame
|
|
25
36
|
## Flame::Application.require_dirs(
|
26
37
|
## %w[config lib models helpers mailers services controllers]
|
27
38
|
## )
|
28
|
-
def require_dirs(dirs)
|
29
|
-
caller_dir = File.dirname caller_file
|
39
|
+
def require_dirs(dirs, ignore: [])
|
30
40
|
dirs.each do |dir|
|
31
|
-
|
32
|
-
.reject { |file| File.executable?(file) }
|
33
|
-
.sort_by { |s| [File.basename(s)[0], s] }
|
34
|
-
.each { |file| require File.expand_path(file) }
|
41
|
+
require_dir File.join(root_dir, dir), ignore: ignore
|
35
42
|
end
|
36
43
|
end
|
37
44
|
|
38
|
-
## Generating application config when inherited
|
39
|
-
def inherited(app)
|
40
|
-
app.config = Config.new(
|
41
|
-
app,
|
42
|
-
default_config_dirs(
|
43
|
-
root_dir: File.dirname(caller_file)
|
44
|
-
).merge(
|
45
|
-
environment: ENV['RACK_ENV'] || 'development'
|
46
|
-
)
|
47
|
-
)
|
48
|
-
end
|
49
|
-
|
50
45
|
## Make available `run Application` without `.new` for `rackup`
|
51
46
|
def call(env)
|
52
47
|
@app ||= new
|
53
48
|
@app.call env
|
54
49
|
end
|
55
50
|
|
51
|
+
using GorillaPatch::DeepDup
|
52
|
+
|
56
53
|
## Build a path to the given controller and action
|
57
54
|
##
|
58
55
|
## @param ctrl [Flame::Controller] class of controller
|
@@ -61,32 +58,45 @@ module Flame
|
|
61
58
|
## @return [String] path for requested method, controller and parameters
|
62
59
|
## @example Path for `show(id)` method of `ArticlesController`
|
63
60
|
## path_to ArticlesController, :show, id: 2 # => "/articles/show/2"
|
64
|
-
## @example Path for `new` method of `ArticlesController` with
|
65
|
-
## path_to ArticlesController, :new,
|
61
|
+
## @example Path for `new` method of `ArticlesController` with query
|
62
|
+
## path_to ArticlesController, :new, author_id: 1
|
66
63
|
## # => "/articles/new?author_id=1"
|
67
64
|
def path_to(ctrl, action = :index, args = {})
|
68
65
|
path = router.path_of(ctrl, action)
|
66
|
+
|
69
67
|
raise Errors::RouteNotFoundError.new(ctrl, action) unless path
|
70
|
-
|
71
|
-
|
68
|
+
|
69
|
+
args = args.deep_dup
|
72
70
|
path = path.assign_arguments(args)
|
73
71
|
path = '/' if path.empty?
|
72
|
+
query = Rack::Utils.build_nested_query args unless args.empty?
|
74
73
|
Addressable::URI.new(path: path, query: query).to_s
|
75
74
|
end
|
76
75
|
|
76
|
+
protected
|
77
|
+
|
78
|
+
attr_accessor :root_dir
|
79
|
+
|
77
80
|
private
|
78
81
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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) }
|
83
93
|
end
|
84
94
|
|
85
95
|
## Mount controller in application class
|
86
96
|
## @param controller [Symbol] the snake-cased name of mounted controller
|
87
97
|
## (without `Controller` or `::IndexController` for namespaces)
|
88
98
|
## @param path [String, nil] root path for the mounted controller
|
89
|
-
## @yield refine defaults
|
99
|
+
## @yield refine defaults paths for a methods of the mounted controller
|
90
100
|
## @example Mount controller with defaults
|
91
101
|
## mount :articles # ArticlesController
|
92
102
|
## @example Mount controller with specific path
|
@@ -101,38 +111,29 @@ module Flame
|
|
101
111
|
## mount :cabinet do # Cabinet::IndexController
|
102
112
|
## mount :articles # Cabinet::ArticlesController
|
103
113
|
## end
|
104
|
-
def mount(
|
114
|
+
def mount(controller, path = nil, nested: true, &block)
|
105
115
|
## Add routes from controller to glob array
|
106
116
|
router.add Router::RoutesRefine.new(
|
107
|
-
|
117
|
+
namespace_name, controller, path, nested: nested, &block
|
108
118
|
)
|
109
119
|
end
|
110
120
|
|
111
121
|
using GorillaPatch::Namespace
|
112
122
|
|
113
|
-
def
|
123
|
+
def namespace_name
|
114
124
|
namespace = self
|
115
125
|
while namespace.name.nil? && namespace.superclass != Flame::Application
|
116
126
|
namespace = superclass
|
117
127
|
end
|
118
128
|
namespace.deconstantize
|
119
129
|
end
|
120
|
-
|
121
|
-
## Initialize default for config directories
|
122
|
-
def default_config_dirs(root_dir:)
|
123
|
-
result = { root_dir: File.realpath(root_dir) }
|
124
|
-
%i[public views config tmp].each do |key|
|
125
|
-
result[:"#{key}_dir"] = proc { File.join(config[:root_dir], key.to_s) }
|
126
|
-
end
|
127
|
-
result
|
128
|
-
end
|
129
130
|
end
|
130
131
|
|
131
132
|
def initialize(app = nil)
|
132
133
|
@app = app
|
133
134
|
end
|
134
135
|
|
135
|
-
## Request
|
136
|
+
## Request receiving method
|
136
137
|
def call(env)
|
137
138
|
@app.call(env) if @app.respond_to? :call
|
138
139
|
Flame::Dispatcher.new(self.class, env).run!
|
data/lib/flame/config.rb
ADDED
@@ -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.fetch('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, aliases: true)
|
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
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gorilla_patch/slice'
|
4
|
+
|
5
|
+
module Flame
|
6
|
+
class Controller
|
7
|
+
## Module for work with actions
|
8
|
+
module Actions
|
9
|
+
include Memery
|
10
|
+
using GorillaPatch::Slice
|
11
|
+
|
12
|
+
## Shortcut for not-inherited public methods: actions
|
13
|
+
## @return [Array<Symbol>] array of actions (public instance methods)
|
14
|
+
def actions
|
15
|
+
public_instance_methods(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
## Re-define public instance methods (actions) from parent
|
19
|
+
## @param actions [Array<Symbol>] Actions for inheritance
|
20
|
+
## @param exclude [Array<Symbol>] Actions for excluding from inheritance
|
21
|
+
## @param from [Module]
|
22
|
+
## Module (or Class) from which actions will be inherited
|
23
|
+
## @example Inherit all parent actions
|
24
|
+
## class MyController < BaseController
|
25
|
+
## inherit_actions
|
26
|
+
## end
|
27
|
+
## @example Inherit certain parent actions
|
28
|
+
## class MyController < BaseController
|
29
|
+
## inherit_actions %i[index show]
|
30
|
+
## end
|
31
|
+
## @example Inherit all parent actions exclude certain
|
32
|
+
## class MyController < BaseController
|
33
|
+
## inherit_actions exclude: %i[edit update]
|
34
|
+
## end
|
35
|
+
## @example Inherit certain actions from specific module
|
36
|
+
## class MyController < BaseController
|
37
|
+
## inherit_actions %i[index show], from: ModuleWithActions
|
38
|
+
## end
|
39
|
+
def inherit_actions(actions = nil, exclude: [], from: superclass)
|
40
|
+
actions = from.actions if actions.nil?
|
41
|
+
actions -= exclude
|
42
|
+
|
43
|
+
actions.each do |action|
|
44
|
+
define_method action, from.public_instance_method(action)
|
45
|
+
end
|
46
|
+
|
47
|
+
return unless from.respond_to?(:refined_http_methods)
|
48
|
+
|
49
|
+
refined_http_methods.merge!(
|
50
|
+
from.refined_http_methods.slice(*actions)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
## Re-define public instance method from module
|
55
|
+
## @param mod [Module] Module for including to controller
|
56
|
+
## @param exclude [Array<Symbol>] Actions for excluding
|
57
|
+
## from module public instance methods
|
58
|
+
## @param only [Array<Symbol>] Actions for re-defining
|
59
|
+
## from module public instance methods
|
60
|
+
## @example Define actions from module in controller
|
61
|
+
## class MyController < BaseController
|
62
|
+
## include with_actions Module1
|
63
|
+
## include with_actions Module2
|
64
|
+
## ....
|
65
|
+
## end
|
66
|
+
## @example Define actions from module exclude some actions in controller
|
67
|
+
## class MyController < BaseController
|
68
|
+
## include with_actions Module1, exclude: %i[action1 action2 ...]
|
69
|
+
## include with_actions Module2, exclude: %i[action1 action2 ...]
|
70
|
+
## ....
|
71
|
+
## end
|
72
|
+
## @example Define actions from module according list in controller
|
73
|
+
## class MyController < BaseController
|
74
|
+
## include with_actions Module1, only: %i[action1 action2 ...]
|
75
|
+
## include with_actions Module2, only: %i[action1 action2 ...]
|
76
|
+
## ....
|
77
|
+
## end
|
78
|
+
def with_actions(mod, exclude: [], only: nil)
|
79
|
+
Module.new do
|
80
|
+
@mod = mod
|
81
|
+
@actions = only || (@mod.public_instance_methods(false) - exclude)
|
82
|
+
|
83
|
+
extend ModuleWithActions
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
memoize def refined_http_methods
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
Flame::Router::HTTP_METHODS.each do |http_method|
|
94
|
+
downcased_http_method = http_method.downcase
|
95
|
+
define_method(
|
96
|
+
downcased_http_method
|
97
|
+
) do |action_or_action_path, action = nil|
|
98
|
+
action, action_path =
|
99
|
+
if action
|
100
|
+
[action, action_or_action_path]
|
101
|
+
else
|
102
|
+
[action_or_action_path, nil]
|
103
|
+
end
|
104
|
+
refined_http_methods[action] = [downcased_http_method, action_path]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
## Base module for module `with_actions`
|
109
|
+
module ModuleWithActions
|
110
|
+
using GorillaPatch::Slice
|
111
|
+
|
112
|
+
def included(ctrl)
|
113
|
+
ctrl.include @mod
|
114
|
+
|
115
|
+
ctrl.inherit_actions @actions, from: @mod
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private_constant :ModuleWithActions
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Flame
|
4
|
-
class
|
4
|
+
class Controller
|
5
5
|
## Helper class for cookies
|
6
6
|
class Cookies
|
7
7
|
## Create an instance of Cookies
|
@@ -22,14 +22,19 @@ module Flame
|
|
22
22
|
|
23
23
|
## Set (or delete) cookies for response
|
24
24
|
## @param key [String, Symbol] name of cookie
|
25
|
-
## @param new_value [Object, nil] value of cookie
|
25
|
+
## @param new_value [Object, Hash, nil] value of cookie or Hash with `:value` and options
|
26
26
|
## @example Set new value to `cat` cookie
|
27
27
|
## cookies['cat'] = 'nice cat'
|
28
|
+
## @example Set new value to `cat` cookie with `Max-Age` 60 seconds
|
29
|
+
## cookies['cat'] = { value: 'nice cat', max_age: 60 }
|
28
30
|
## @example Delete `cat` cookie
|
29
31
|
## cookies['cat'] = nil
|
30
32
|
def []=(key, new_value)
|
31
|
-
|
33
|
+
case new_value
|
34
|
+
when NilClass
|
32
35
|
@response.delete_cookie(key.to_s, path: '/')
|
36
|
+
when Hash
|
37
|
+
@response.set_cookie(key.to_s, new_value)
|
33
38
|
else
|
34
39
|
@response.set_cookie(key.to_s, value: new_value, path: '/')
|
35
40
|
end
|
@@ -4,24 +4,19 @@ module Flame
|
|
4
4
|
class Controller
|
5
5
|
## Module with methods for path or URL building
|
6
6
|
module PathTo
|
7
|
+
include Memery
|
8
|
+
|
7
9
|
## Look documentation at {Flame::Dispatcher#path_to}
|
8
10
|
def path_to(*args)
|
9
11
|
add_controller_class(args)
|
12
|
+
add_controller_arguments(args)
|
10
13
|
@dispatcher.path_to(*args)
|
11
14
|
end
|
12
15
|
|
13
16
|
## Build a URI to the given controller and action, or path
|
14
17
|
def url_to(*args, **options)
|
15
|
-
|
16
|
-
path
|
17
|
-
if first_arg.is_a?(String) || first_arg.is_a?(Flame::Path)
|
18
|
-
find_static(first_arg).path(with_version: options[:version])
|
19
|
-
else
|
20
|
-
path_to(*args, **options)
|
21
|
-
end
|
22
|
-
Addressable::URI.new(
|
23
|
-
scheme: request.scheme, host: request.host_with_port, path: path
|
24
|
-
).to_s
|
18
|
+
path = build_path_for_url(*args, **options)
|
19
|
+
Addressable::URI.join(request.base_url, path).to_s
|
25
20
|
end
|
26
21
|
|
27
22
|
using GorillaPatch::Namespace
|
@@ -31,9 +26,38 @@ module Flame
|
|
31
26
|
def path_to_back
|
32
27
|
back_path = request.referer
|
33
28
|
return back_path if back_path && back_path != request.url
|
29
|
+
|
34
30
|
return path_to :index if self.class.actions.include?(:index)
|
31
|
+
|
35
32
|
'/'
|
36
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def add_controller_class(args)
|
38
|
+
args.unshift(self.class) if args[0].is_a?(Symbol)
|
39
|
+
args.insert(1, :index) if args[0].is_a?(Class) && !args[1].is_a?(Symbol)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_controller_arguments(args)
|
43
|
+
if args[-1].is_a?(Hash)
|
44
|
+
args[-1] = controller_arguments.merge args[-1]
|
45
|
+
else
|
46
|
+
args.push(controller_arguments)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_path_for_url(*args, **options)
|
51
|
+
first_arg = args.first
|
52
|
+
if first_arg.is_a?(String) || first_arg.is_a?(Flame::Path)
|
53
|
+
find_static(first_arg).path(with_version: options[:version])
|
54
|
+
else
|
55
|
+
path_to(*args, **options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
memoize :build_path_for_url,
|
60
|
+
condition: -> { config[:environment] == 'production' }
|
37
61
|
end
|
38
62
|
end
|
39
63
|
end
|