mvc_one 0.1.0.pre.rc3 → 0.1.0.pre.rc5
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 +4 -4
- data/.rubocop.yml +32 -9
- data/Gemfile +7 -6
- data/Gemfile.lock +89 -3
- data/Rakefile +3 -3
- data/exe/mvc_one +23 -11
- data/lib/mvc_one/application.rb +56 -0
- data/lib/mvc_one/contracts/application_contract.rb +5 -1
- data/lib/mvc_one/controllers/application_controller.rb +50 -48
- data/lib/mvc_one/models/application_model.rb +3 -1
- data/lib/mvc_one/regexp_router.rb +89 -85
- data/lib/mvc_one/repositories/application_relation.rb +24 -22
- data/lib/mvc_one/repositories/application_repository.rb +13 -9
- data/lib/mvc_one/serializers/application_serializer.rb +100 -98
- data/lib/mvc_one/version.rb +1 -1
- data/lib/mvc_one.rb +3 -3
- data/mvc_one.gemspec +25 -24
- data/templates/config/routes_rb.erb +2 -0
- data/templates/general/application_rb.erb +1 -37
- data/templates/general/config_ru.erb +0 -1
- data/templates/general/database_yml.erb +2 -2
- data/templates/general/gemfile.erb +3 -1
- data/templates/general/secrets_yml.erb +2 -2
- metadata +54 -52
@@ -8,120 +8,124 @@
|
|
8
8
|
# get, post, delete, etc - HTTP methods
|
9
9
|
# '/user', '/users/:id' - regexp, :id - params, available with result
|
10
10
|
# to: 'web#users#create' - route request to Web::UsersController create method
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
11
|
+
module MvcOne
|
12
|
+
class RegexpRouter
|
13
|
+
HTTP_METHODS = %w[get post patch put delete options head].freeze
|
14
|
+
|
15
|
+
# concrete route
|
16
|
+
class Route
|
17
|
+
ANY_METHOD = 'any'
|
18
|
+
attr_reader :controller, :action, :pattern, :method
|
19
|
+
|
20
|
+
def initialize(controller:, action:, pattern:, method:)
|
21
|
+
@controller = controller
|
22
|
+
@action = action
|
23
|
+
@pattern = pattern
|
24
|
+
@method = method
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
def matched?(path, http_method)
|
28
|
+
return true if method == ANY_METHOD
|
29
|
+
return false unless method.to_s == http_method.to_s
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
pattern_split = pattern.split('/')
|
32
|
+
path_split = path.split('/')
|
33
|
+
return false if pattern_split.length != path_split.length
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
pattern_matched_with?(pattern_split, path_split)
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def params(path)
|
39
|
+
pattern_split = pattern.split('/')
|
40
|
+
path_split = path.split('/')
|
40
41
|
|
41
|
-
|
42
|
+
result = {}
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
pattern_split.each_with_index do |e, index|
|
45
|
+
if e.start_with?(':')
|
46
|
+
key = e.delete(':').to_sym
|
47
|
+
result[key] = path_split[index]
|
48
|
+
end
|
47
49
|
end
|
48
|
-
end
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
result
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
+
private
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
def pattern_matched_with?(pattern_split, path_split)
|
57
|
+
matched = true
|
58
|
+
pattern_split.each_with_index do |e, index|
|
59
|
+
next if e.start_with?(':')
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
if e != path_split[index]
|
62
|
+
matched = false
|
63
|
+
break
|
64
|
+
end
|
63
65
|
end
|
64
|
-
end
|
65
66
|
|
66
|
-
|
67
|
+
matched
|
68
|
+
end
|
67
69
|
end
|
68
|
-
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
# result of searching right route, has controller, action, params from path
|
72
|
+
class Result
|
73
|
+
attr_reader :route, :path
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
def initialize(route, path)
|
76
|
+
@route = route
|
77
|
+
@path = path
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
def controller
|
81
|
+
instance_eval "#{route.controller}Controller", __FILE__, __LINE__ # Return class of matched controller
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
84
|
+
def action
|
85
|
+
route.action
|
86
|
+
end
|
86
87
|
|
87
|
-
|
88
|
-
|
88
|
+
def params
|
89
|
+
route.params(path)
|
90
|
+
end
|
89
91
|
end
|
90
|
-
end
|
91
92
|
|
92
|
-
|
93
|
+
attr_reader :routes
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
95
|
+
def initialize(route_path)
|
96
|
+
@routes = []
|
97
|
+
load_routes(route_path)
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
-
|
100
|
+
def resolve(path, method)
|
101
|
+
route = routes.find { |r| r.matched?(path, method.downcase) }
|
101
102
|
|
102
|
-
|
103
|
-
|
103
|
+
Result.new(route, path) if route
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
104
107
|
|
105
|
-
|
108
|
+
def load_routes(route_path)
|
109
|
+
return unless File.file?(route_path)
|
106
110
|
|
107
|
-
|
108
|
-
|
109
|
-
end
|
111
|
+
instance_eval(File.read(route_path), route_path.to_s).call
|
112
|
+
end
|
110
113
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
+
HTTP_METHODS.each do |method|
|
115
|
+
define_method method do |pattern, options|
|
116
|
+
register_route(method, pattern, options)
|
117
|
+
end
|
114
118
|
end
|
115
|
-
end
|
116
119
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
def any(to:)
|
121
|
+
register_route('any', '', to: to)
|
122
|
+
end
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
124
|
+
def register_route(method, pattern, options)
|
125
|
+
path_pieces = options[:to].split('#')
|
126
|
+
controller = path_pieces.slice(0..-2).map(&:capitalize).join('::')
|
127
|
+
action = path_pieces.last
|
128
|
+
@routes << Route.new(controller: controller, action: action, pattern: pattern, method: method)
|
129
|
+
end
|
126
130
|
end
|
127
131
|
end
|
@@ -1,35 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Base relation object
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module MvcOne
|
5
|
+
class ApplicationRelation
|
6
|
+
def self.wrap(klass)
|
7
|
+
define_method :wrapped_class do
|
8
|
+
instance_eval klass.to_s.capitalize, __FILE__, __LINE__ # Return wrapped class
|
9
|
+
end
|
8
10
|
end
|
9
|
-
end
|
10
11
|
|
11
|
-
|
12
|
+
attr_reader :target, :attributes
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
def initialize(attributes)
|
15
|
+
raise 'Please define wrapped class with wrap class method' unless respond_to?(:wrapped_class)
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
@target = wrapped_class.new(attributes)
|
18
|
+
@attributes = attributes
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def method_missing(method)
|
22
|
+
target.send(method)
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def respond_to?(method)
|
26
|
+
methods.include?(method) || target.respond_to?(method)
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def respond_to_missing?(method)
|
30
|
+
target.respond_to_missing?(method)
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
def class
|
34
|
+
target.class
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
@@ -1,16 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'sequel'
|
4
|
+
|
3
5
|
# Main gateway for persisted storage
|
4
|
-
|
5
|
-
class
|
6
|
+
module MvcOne
|
7
|
+
class ApplicationRepository
|
8
|
+
class NotFoundRecord < StandardError; end
|
6
9
|
|
7
|
-
|
10
|
+
attr_reader :table_name
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
def self.db_config
|
13
|
+
Application::Config.db_config
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
DB = Sequel.connect(db_config['db']['connection_line'])
|
17
|
+
DB.loggers << Logger.new($stdout)
|
18
|
+
DB.sql_log_level = :debug
|
19
|
+
end
|
16
20
|
end
|
@@ -1,141 +1,143 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# All app serializers must be subclasses of ApplicationSerializer
|
4
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
module MvcOne
|
5
|
+
class ApplicationSerializer
|
6
|
+
class << self
|
7
|
+
def attributes(*args)
|
8
|
+
define_method :attributes do
|
9
|
+
args
|
10
|
+
end
|
9
11
|
end
|
10
|
-
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def type(type)
|
14
|
+
define_method :type do
|
15
|
+
type.to_s
|
16
|
+
end
|
15
17
|
end
|
16
|
-
end
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
def id
|
20
|
+
raise ArgumentError, 'No block given' unless block_given?
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
define_method :id do
|
23
|
+
yield data
|
24
|
+
end
|
23
25
|
end
|
24
|
-
end
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
def links
|
28
|
+
raise ArgumentError, 'No block given' unless block_given?
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
define_method :links do
|
31
|
+
yield data
|
32
|
+
end
|
31
33
|
end
|
32
|
-
end
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
def has_one(relation)
|
36
|
+
raise ArgumentError, 'No block given' unless block_given?
|
36
37
|
|
37
|
-
|
38
|
-
|
38
|
+
define_method relation do
|
39
|
+
yield data
|
40
|
+
end
|
39
41
|
end
|
40
42
|
end
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
+
attr_reader :data
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
def initialize(data, include: [])
|
47
|
+
@data = data
|
48
|
+
@include = include
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
51
|
+
def serialize
|
52
|
+
result = {}
|
53
|
+
if data.is_a?(Enumerable)
|
54
|
+
result[:data] = data.map { |e| self.class.new(e, include: @include).serialize_data }
|
55
|
+
included = data.flat_map { |e| self.class.new(e, include: @include).included_serialization }.compact
|
56
|
+
result[:included] = included if included.any?
|
57
|
+
else
|
58
|
+
result[:data] = data_serialization
|
59
|
+
result[:included] = included_serialization if included_serialization
|
60
|
+
end
|
61
|
+
result
|
59
62
|
end
|
60
|
-
result
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
def serialize_data
|
65
|
+
data_serialization
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
def data_serialization
|
69
|
+
raise NoMethodError if data.is_a?(Enumerable)
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
result = {}
|
72
|
+
add_type(result)
|
73
|
+
add_id(result)
|
74
|
+
add_attributes(result)
|
75
|
+
add_links(result)
|
76
|
+
add_relatioship(result)
|
77
|
+
result
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
-
|
80
|
+
def included_serialization
|
81
|
+
return nil if @include.empty?
|
81
82
|
|
82
|
-
|
83
|
-
|
83
|
+
@include.map do |include|
|
84
|
+
raise(NoMethodError, "Undefined #{include} include") unless respond_to?(include)
|
84
85
|
|
85
|
-
|
86
|
+
send(include)
|
87
|
+
end
|
86
88
|
end
|
87
|
-
end
|
88
89
|
|
89
|
-
|
90
|
+
private
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
def add_relatioship(result)
|
93
|
+
result[:relationships] = {} unless @include.empty?
|
94
|
+
@include.each do |include|
|
95
|
+
raise(NoMethodError, "Undefined #{include} include") unless respond_to?(include)
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
included_entity = send(include)
|
98
|
+
included_relationship = {}
|
99
|
+
included_relationship[:data] = included_entity.slice(:id, :type)
|
100
|
+
result[:relationships][include] = included_relationship
|
101
|
+
end
|
100
102
|
end
|
101
|
-
end
|
102
103
|
|
103
|
-
|
104
|
-
|
105
|
-
|
104
|
+
def add_links(result)
|
105
|
+
result[:links] = links if respond_to?(:links)
|
106
|
+
end
|
106
107
|
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
def add_type(result)
|
109
|
+
result[:type] = respond_to?(:type) ? type : default_type
|
110
|
+
end
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
112
|
+
def add_id(result)
|
113
|
+
result[:id] = respond_to?(:id) ? id : default_id
|
114
|
+
end
|
114
115
|
|
115
|
-
|
116
|
-
|
116
|
+
def add_attributes(result)
|
117
|
+
return unless respond_to?(:attributes)
|
117
118
|
|
118
|
-
|
119
|
-
|
119
|
+
result_attributes = attributes.each_with_object({}) do |attr, acc|
|
120
|
+
acc[attr] = get_attribute(attr)
|
121
|
+
end
|
122
|
+
result[:attributes] = result_attributes
|
120
123
|
end
|
121
|
-
result[:attributes] = result_attributes
|
122
|
-
end
|
123
124
|
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
def default_type
|
126
|
+
data.class.to_s.downcase
|
127
|
+
end
|
127
128
|
|
128
|
-
|
129
|
-
|
130
|
-
|
129
|
+
def default_id
|
130
|
+
data.id.to_s
|
131
|
+
end
|
131
132
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
133
|
+
def get_attribute(attr)
|
134
|
+
if respond_to?(attr)
|
135
|
+
send(attr)
|
136
|
+
elsif data.respond_to?(attr)
|
137
|
+
data.send(attr)
|
138
|
+
else
|
139
|
+
raise NoMethodError, "Undefined serialize key #{attr}"
|
140
|
+
end
|
139
141
|
end
|
140
142
|
end
|
141
143
|
end
|
data/lib/mvc_one/version.rb
CHANGED
data/lib/mvc_one.rb
CHANGED
data/mvc_one.gemspec
CHANGED
@@ -1,45 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative 'lib/mvc_one/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'mvc_one'
|
7
7
|
spec.version = MvcOne::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['Evgenii Sendziuk']
|
9
|
+
spec.email = ['evgeniisendziuk@taxdome.com']
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.license =
|
14
|
-
spec.required_ruby_version =
|
11
|
+
spec.summary = 'Simple mvc framework, not for production purposes'
|
12
|
+
spec.description = 'Simple mvc framework, not for production purposes'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.required_ruby_version = '>= 2.6.0'
|
15
15
|
|
16
|
-
|
17
|
-
spec.metadata[
|
18
|
-
spec.metadata["changelog_uri"] = "https://github.com/senzpo/mvc_one.git"
|
16
|
+
spec.metadata['source_code_uri'] = 'https://github.com/senzpo/mvc_one.git'
|
17
|
+
spec.metadata['changelog_uri'] = 'https://github.com/senzpo/mvc_one.git'
|
19
18
|
|
20
19
|
# Specify which files should be added to the gem when it is released.
|
21
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
21
|
spec.files = Dir.chdir(__dir__) do
|
23
22
|
`git ls-files -z`.split("\x0").reject do |f|
|
24
|
-
(File.expand_path(f) == __FILE__) ||
|
23
|
+
(File.expand_path(f) == __FILE__) ||
|
24
|
+
f.match(%r{\A(?:(?:bin|test|spec|features|config)/|\.(?:git|circleci)|appveyor)})
|
25
25
|
end
|
26
26
|
end
|
27
|
-
spec.bindir =
|
27
|
+
spec.bindir = 'exe'
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
-
spec.require_paths = [
|
29
|
+
spec.require_paths = ['lib']
|
30
30
|
|
31
31
|
# Uncomment to register a new dependency of your gem
|
32
|
-
spec.add_dependency
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
38
|
-
spec.add_dependency
|
39
|
-
spec.add_dependency
|
40
|
-
spec.add_dependency
|
41
|
-
spec.add_dependency
|
32
|
+
spec.add_dependency 'bcrypt', '~> 3.1'
|
33
|
+
spec.add_dependency 'dry-struct', '~> 1.5'
|
34
|
+
spec.add_dependency 'dry-transaction', '~> 0.14'
|
35
|
+
spec.add_dependency 'dry-validation', '~> 1.9'
|
36
|
+
spec.add_dependency 'rack', '~> 3'
|
37
|
+
spec.add_dependency 'rack-session', '~> 2'
|
38
|
+
spec.add_dependency 'sequel', '~> 5.60'
|
39
|
+
spec.add_dependency 'slim', '~> 5'
|
40
|
+
spec.add_dependency 'sqlite3', '~> 1.5'
|
41
|
+
spec.add_dependency 'thor', '~> 1.2'
|
42
42
|
|
43
43
|
# For more information and examples about making a new gem, check out our
|
44
44
|
# guide at: https://bundler.io/guides/creating_gem.html
|
45
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
45
46
|
end
|