mvc_one 0.1.0.pre.rc1 → 0.1.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83b06832bc6afc138ec694b723c2bc13f2f39827fb46770d7773a3048d91c42f
4
- data.tar.gz: bd99db661af0cede4f3f65ecbca91ac20c9d933cb8f3ab52ee2a03649d0d4068
3
+ metadata.gz: a9156a3747a1449345c6139d1759d2b4666d5eb449daf2eebe37e7f47986d072
4
+ data.tar.gz: 3cf5f42bfe96721e029ca91efcf05a1f358b91e5353dea53fe0fdc557e20c4d1
5
5
  SHA512:
6
- metadata.gz: d84541b370a67866d35f88f52988d5245a705ad78cc7591bc88637237d58b1e96fafb29ec3e430bbe084c9df83c86675b557018cd5549fb8390eeabf04d93034
7
- data.tar.gz: 2faed9bd7a152d766c09c1c157da12f7ea35142d8edbe1e4e6176277c31e6a268ec03bb999693b669daf9c9b4cc84803cdda200ae069d26eca33db9c41045f5c
6
+ metadata.gz: 28398e4661ba3ba1886acb70073eadfe69544cf0bbc61be2ac0d9a6916aa6a52cbe01fbdc3f44c520381a645ea35f13ea46d5af66d3f9e24eff2bc5c6e5b970d
7
+ data.tar.gz: 232cb7052c15497e64225ef73ccef1385eb59f8c23873a64dfa6de18cfae734d08aeb922e1d4bc452156c3b5ecc519e6e7ad2caf11bad7c2c51890e33a8956a9
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Abstract class for Contract realisation in application
4
+ class MvcOne::ApplicationContract < Dry::Validation::Contract
5
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'slim/include'
4
+ require 'json'
5
+
6
+ # All app controllers must be subclasses of ApplicationController
7
+ class MvcOne::ApplicationController
8
+ DEFAULT_LAYOUT = './app/views/application_layout.slim'
9
+ DEFAULT_HTTP_CODE = 200
10
+
11
+ attr_accessor :env
12
+ attr_reader :action, :params, :request
13
+
14
+ def self.before_action(*methods)
15
+ define_method :before_action do
16
+ methods.each { |method| send(method) }
17
+ end
18
+ end
19
+
20
+ def initialize(env, params, request)
21
+ @env = env
22
+ @params = params
23
+ @request = request
24
+ end
25
+
26
+ def resolve(action)
27
+ before_action if respond_to?(:before_action)
28
+
29
+ @action = action
30
+ send(action)
31
+ end
32
+
33
+ def render(code: DEFAULT_HTTP_CODE, headers: {}, body: nil, layout: DEFAULT_LAYOUT, template: nil)
34
+ return [code, headers, [body]] unless body.nil?
35
+
36
+ body = prepare_body(layout, template || template_path(action))
37
+ [code, headers, [body]]
38
+ end
39
+
40
+ def head(code, headers: {})
41
+ [code, headers, []]
42
+ end
43
+
44
+ def request_params
45
+ return @request_params if defined?(@request_params)
46
+ return @request_params = {} if request.body.nil?
47
+
48
+ @request_params =
49
+ case request.content_type
50
+ when 'application/json' then JSON.parse(request.body.read).transform_keys(&:to_sym)
51
+ else request.params.transform_keys(&:to_sym)
52
+ end
53
+ end
54
+
55
+ def render_partial(name, options = {}, &block)
56
+ Slim::Template.new("#{name}.slim", options).render(self, &block)
57
+ end
58
+
59
+ private
60
+
61
+ def template_path(action)
62
+ path = self.class.to_s.delete_suffix('Controller').split('::').map(&:downcase).join('/')
63
+ "./app/views/#{path}/#{action}.slim"
64
+ end
65
+
66
+ def prepare_body(layout, template)
67
+ return Slim::Template.new(template).render(self) if layout.nil?
68
+
69
+ Slim::Template.new(layout).render(self) do
70
+ Slim::Template.new(template).render(self)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+
5
+ module Types
6
+ include Dry.Types()
7
+ end
8
+
9
+ # Domain model
10
+ class MvcOne::ApplicationModel < Dry::Struct
11
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Simple router based on dsl
4
+ # lambda do
5
+ # get '/users/:id', to: 'web#users#show'
6
+ # post '/users', to: 'web#users#create'
7
+ # end
8
+ # get, post, delete, etc - HTTP methods
9
+ # '/user', '/users/:id' - regexp, :id - params, available with result
10
+ # to: 'web#users#create' - route request to Web::UsersController create method
11
+ class MvcOne::RegexpRouter
12
+ HTTP_METHODS = %w[get post patch put delete options head].freeze
13
+
14
+ # concrete route
15
+ class Route
16
+ ANY_METHOD = 'any'
17
+ attr_reader :controller, :action, :pattern, :method
18
+
19
+ def initialize(controller:, action:, pattern:, method:)
20
+ @controller = controller
21
+ @action = action
22
+ @pattern = pattern
23
+ @method = method
24
+ end
25
+
26
+ def matched?(path, http_method)
27
+ return true if method == ANY_METHOD
28
+ return false unless method.to_s == http_method.to_s
29
+
30
+ pattern_split = pattern.split('/')
31
+ path_split = path.split('/')
32
+ return false if pattern_split.length != path_split.length
33
+
34
+ pattern_matched_with?(pattern_split, path_split)
35
+ end
36
+
37
+ def params(path)
38
+ pattern_split = pattern.split('/')
39
+ path_split = path.split('/')
40
+
41
+ result = {}
42
+
43
+ pattern_split.each_with_index do |e, index|
44
+ if e.start_with?(':')
45
+ key = e.delete(':').to_sym
46
+ result[key] = path_split[index]
47
+ end
48
+ end
49
+
50
+ result
51
+ end
52
+
53
+ private
54
+
55
+ def pattern_matched_with?(pattern_split, path_split)
56
+ matched = true
57
+ pattern_split.each_with_index do |e, index|
58
+ next if e.start_with?(':')
59
+
60
+ if e != path_split[index]
61
+ matched = false
62
+ break
63
+ end
64
+ end
65
+
66
+ matched
67
+ end
68
+ end
69
+
70
+ # result of searching right route, has controller, action, params from path
71
+ class Result
72
+ attr_reader :route, :path
73
+
74
+ def initialize(route, path)
75
+ @route = route
76
+ @path = path
77
+ end
78
+
79
+ def controller
80
+ instance_eval "#{route.controller}Controller", __FILE__, __LINE__ # Return class of matched controller
81
+ end
82
+
83
+ def action
84
+ route.action
85
+ end
86
+
87
+ def params
88
+ route.params(path)
89
+ end
90
+ end
91
+
92
+ attr_reader :routes
93
+
94
+ def initialize(route_path)
95
+ @routes = []
96
+ load_routes(route_path)
97
+ end
98
+
99
+ def resolve(path, method)
100
+ route = routes.find { |r| r.matched?(path, method.downcase) }
101
+
102
+ Result.new(route, path) if route
103
+ end
104
+
105
+ private
106
+
107
+ def load_routes(route_path)
108
+ instance_eval(File.read(route_path), route_path.to_s).call
109
+ end
110
+
111
+ HTTP_METHODS.each do |method|
112
+ define_method method do |pattern, options|
113
+ register_route(method, pattern, options)
114
+ end
115
+ end
116
+
117
+ def any(to:)
118
+ register_route('any', '', to: to)
119
+ end
120
+
121
+ def register_route(method, pattern, options)
122
+ path_pieces = options[:to].split('#')
123
+ controller = path_pieces.slice(0..-2).map(&:capitalize).join('::')
124
+ action = path_pieces.last
125
+ @routes << Route.new(controller: controller, action: action, pattern: pattern, method: method)
126
+ end
127
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base relation object
4
+ class MvcOne::ApplicationRelation
5
+ def self.wrap(klass)
6
+ define_method :wrapped_class do
7
+ instance_eval klass.to_s.capitalize, __FILE__, __LINE__ # Return wrapped class
8
+ end
9
+ end
10
+
11
+ attr_reader :target, :attributes
12
+
13
+ def initialize(attributes)
14
+ raise 'Please define wrapped class with wrap class method' unless respond_to?(:wrapped_class)
15
+
16
+ @target = wrapped_class.new(attributes)
17
+ @attributes = attributes
18
+ end
19
+
20
+ def method_missing(method)
21
+ target.send(method)
22
+ end
23
+
24
+ def respond_to?(method)
25
+ methods.include?(method) || target.respond_to?(method)
26
+ end
27
+
28
+ def respond_to_missing?(method)
29
+ target.respond_to_missing?(method)
30
+ end
31
+
32
+ def class
33
+ target.class
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Main gateway for persisted storage
4
+ class MvcOne::ApplicationRepository
5
+ class NotFoundRecord < StandardError; end
6
+
7
+ attr_reader :table_name
8
+
9
+ def self.db_config
10
+ YAML.load_file('config/database.yml')[Application::Config.env]
11
+ end
12
+
13
+ DB = Sequel.connect(db_config['db']['connection_line'])
14
+ DB.loggers << Logger.new($stdout)
15
+ DB.sql_log_level = :debug
16
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ # All app serializers must be subclasses of ApplicationSerializer
4
+ class MvcOne::ApplicationSerializer
5
+ class << self
6
+ def attributes(*args)
7
+ define_method :attributes do
8
+ args
9
+ end
10
+ end
11
+
12
+ def type(type)
13
+ define_method :type do
14
+ type.to_s
15
+ end
16
+ end
17
+
18
+ def id
19
+ raise ArgumentError, 'No block given' unless block_given?
20
+
21
+ define_method :id do
22
+ yield data
23
+ end
24
+ end
25
+
26
+ def links
27
+ raise ArgumentError, 'No block given' unless block_given?
28
+
29
+ define_method :links do
30
+ yield data
31
+ end
32
+ end
33
+
34
+ def has_one(relation)
35
+ raise ArgumentError, 'No block given' unless block_given?
36
+
37
+ define_method relation do
38
+ yield data
39
+ end
40
+ end
41
+ end
42
+
43
+ attr_reader :data
44
+
45
+ def initialize(data, include: [])
46
+ @data = data
47
+ @include = include
48
+ end
49
+
50
+ def serialize
51
+ result = {}
52
+ if data.is_a?(Enumerable)
53
+ result[:data] = data.map { |e| self.class.new(e, include: @include).serialize_data }
54
+ included = data.flat_map { |e| self.class.new(e, include: @include).included_serialization }.compact
55
+ result[:included] = included if included.any?
56
+ else
57
+ result[:data] = data_serialization
58
+ result[:included] = included_serialization if included_serialization
59
+ end
60
+ result
61
+ end
62
+
63
+ def serialize_data
64
+ data_serialization
65
+ end
66
+
67
+ def data_serialization
68
+ raise NoMethodError if data.is_a?(Enumerable)
69
+
70
+ result = {}
71
+ add_type(result)
72
+ add_id(result)
73
+ add_attributes(result)
74
+ add_links(result)
75
+ add_relatioship(result)
76
+ result
77
+ end
78
+
79
+ def included_serialization
80
+ return nil if @include.empty?
81
+
82
+ @include.map do |include|
83
+ raise(NoMethodError, "Undefined #{include} include") unless respond_to?(include)
84
+
85
+ send(include)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def add_relatioship(result)
92
+ result[:relationships] = {} unless @include.empty?
93
+ @include.each do |include|
94
+ raise(NoMethodError, "Undefined #{include} include") unless respond_to?(include)
95
+
96
+ included_entity = send(include)
97
+ included_relationship = {}
98
+ included_relationship[:data] = included_entity.slice(:id, :type)
99
+ result[:relationships][include] = included_relationship
100
+ end
101
+ end
102
+
103
+ def add_links(result)
104
+ result[:links] = links if respond_to?(:links)
105
+ end
106
+
107
+ def add_type(result)
108
+ result[:type] = respond_to?(:type) ? type : default_type
109
+ end
110
+
111
+ def add_id(result)
112
+ result[:id] = respond_to?(:id) ? id : default_id
113
+ end
114
+
115
+ def add_attributes(result)
116
+ return unless respond_to?(:attributes)
117
+
118
+ result_attributes = attributes.each_with_object({}) do |attr, acc|
119
+ acc[attr] = get_attribute(attr)
120
+ end
121
+ result[:attributes] = result_attributes
122
+ end
123
+
124
+ def default_type
125
+ data.class.to_s.downcase
126
+ end
127
+
128
+ def default_id
129
+ data.id.to_s
130
+ end
131
+
132
+ def get_attribute(attr)
133
+ if respond_to?(attr)
134
+ send(attr)
135
+ elsif data.respond_to?(attr)
136
+ data.send(attr)
137
+ else
138
+ raise NoMethodError, "Undefined serialize key #{attr}"
139
+ end
140
+ end
141
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MvcOne
4
- VERSION = "0.1.0-rc1"
4
+ VERSION = "0.1.0-rc2"
5
5
  end
@@ -0,0 +1,12 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Show page
5
+ meta name="keywords" content="template language"
6
+ meta name="author"
7
+ link rel="icon" type="image/png"
8
+ / javascript:
9
+ // alert('Slim supports embedded javascript!')
10
+
11
+ body
12
+ h1 404
data/mvc_one.gemspec CHANGED
@@ -30,6 +30,15 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
32
  spec.add_dependency "thor"
33
+ spec.add_dependency "slim"
34
+ spec.add_dependency "bcrypt"
35
+ spec.add_dependency "rack"
36
+ spec.add_dependency "rack-session"
37
+ spec.add_dependency "sequel"
38
+ spec.add_dependency "sqlite3"
39
+ spec.add_dependency "dry-struct"
40
+ spec.add_dependency "dry-transaction"
41
+ spec.add_dependency "dry-validation"
33
42
 
34
43
  # For more information and examples about making a new gem, check out our
35
44
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mvc_one
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre.rc1
4
+ version: 0.1.0.pre.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgenii Sendziuk
@@ -24,6 +24,132 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slim
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bcrypt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-session
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sequel
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sqlite3
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: dry-struct
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: dry-transaction
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: dry-validation
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
27
153
  description: Simple mvc framework, not for production purposes
28
154
  email:
29
155
  - evgeniisendziuk@taxdome.com
@@ -47,7 +173,15 @@ files:
47
173
  - Rakefile
48
174
  - exe/mvc_one
49
175
  - lib/mvc_one.rb
176
+ - lib/mvc_one/contracts/application_contract.rb
177
+ - lib/mvc_one/controllers/application_controller.rb
178
+ - lib/mvc_one/models/application_model.rb
179
+ - lib/mvc_one/regexp_router.rb
180
+ - lib/mvc_one/repositories/application_relation.rb
181
+ - lib/mvc_one/repositories/application_repository.rb
182
+ - lib/mvc_one/serializers/application_serializer.rb
50
183
  - lib/mvc_one/version.rb
184
+ - lib/mvc_one/views/404.slim
51
185
  - mvc_one.gemspec
52
186
  - sig/mvc_one.rbs
53
187
  - templates/app/controller_template.erb