jet_set 0.4.0 → 0.5.0

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: 4c2b21527242c8574e604c1ece2b26ec71d59c7712b76cdfa9e88fdc6a47265b
4
- data.tar.gz: bdcba0c32be1c7db5a0b63f206dd7c5e354c30a9af14eb1b0b204a55ea4ce349
3
+ metadata.gz: 41fe70bc858f2949d8cf010b6af40066a84bdfdf481f0fef13dfdb49fca1de46
4
+ data.tar.gz: 02c77cd6a4741a0be81caa52e3c3b63b75722cddffbd289497a194701d60c447
5
5
  SHA512:
6
- metadata.gz: b6e155d9e2c20157beb3689764a42f8618159fb443b11c41d2ae81a5405fc3fadb55e31a562fcbf58fb0e06a78bf8611ea10eb36378c2b14e4c3e59c6e5b409b
7
- data.tar.gz: 076a5aa11cb87fa6773faca5d3b92e218e5e939a6808d1825486c8cc1c8428cbeb1b480fe152f69a3e6f09b9f5431b4ebbec70873c567a44e91c6439f8150120
6
+ metadata.gz: 99ca355278cad5427c3b69228806f7405e4725a2d2acf1c5902fdf488a7e2acf5a29ee509e93ace75cdae86ba6a596fa79493d848b62acff90977cf00a04e864
7
+ data.tar.gz: 4008d0a706a8c416d7958c4276b3a494f5190a41df6fc4d4e69b669edf9d5e2f97d4dda73091a3692659a526da060aead126b4c44c747b368bbfa1162c857d2d
data/README.md CHANGED
@@ -28,7 +28,7 @@ Open DB connection, see [Sequel docs](https://sequel.jeremyevans.net/rdoc/files/
28
28
  @connection = Sequel.connect('sqlite:/') # you can connect to any DB supported by Sequel
29
29
  ```
30
30
 
31
- Create a mapping of your model, a details described [here]:
31
+ Create a mapping of your model, details described [here]:
32
32
  ```ruby
33
33
  class Mapping
34
34
  def self.load_mapping
@@ -137,8 +137,41 @@ json = JSON.generate(data: result)
137
137
  In other words, following [CQS](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) approach you can
138
138
  load your model for a command but not for a query.
139
139
 
140
+ ### Validation
141
+ Simple validation is optional feature provided by JetSet out of the box. To add this to your domain object you just need
142
+ to include module `JetSet::Validations` and use `validate` statements:
143
+
144
+ ```ruby
145
+ require 'jet_set/validations'
146
+
147
+ class User
148
+ include JetSet::Validations
149
+ validate :name, 'cannot be empty', -> (value) {!value.nil? && !value.empty?}
150
+ validate :email, type: :string, presence: true
151
+ validate :email, 'should be valid email address', -> (value) {value.match(...)}
152
+
153
+ def initialize(attrs = {})
154
+ @name = attrs[:name]
155
+ end
156
+ end
157
+ ```
158
+ JetSet uses such validations automatically on saving objects in the database. Also you can invoke validation manually,
159
+ i.e. in unit tests, using `validate!` method:
160
+
161
+ ```ruby
162
+ user = User.new(name: nil)
163
+ user.validate! # raises JetSet::ValidationError
164
+ ```
165
+
166
+ `JetSet::ValidationError` contains details regarding invalid items like:
167
+
168
+ ```ruby
169
+ error.invalid_items # => {name: 'cannot be empty'}
170
+ ```
171
+
172
+
173
+
140
174
  You can find more interesting examples in [JetSet integration tests](https://github.com/cylon-v/jet_set/tree/master/spec/integration).
141
- Also for the details please visit our [wiki].
142
175
 
143
176
  ## Development
144
177
 
data/jet_set.gemspec CHANGED
@@ -30,11 +30,11 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ['lib']
32
32
 
33
- spec.add_dependency 'sequel', '~> 5.4.0'
34
- spec.add_dependency 'hypo', '~> 0.10.0'
35
- spec.add_development_dependency 'bundler', '~> 1.15'
36
- spec.add_development_dependency 'rake', '~> 10.0'
37
- spec.add_development_dependency 'rspec', '~> 3.0'
38
- spec.add_development_dependency 'sqlite3', '~> 1.3'
39
- spec.add_development_dependency 'simplecov', '~> 0.16'
33
+ spec.add_dependency 'sequel', '>= 5.4.0'
34
+ spec.add_dependency 'hypo', '>= 1.0.0'
35
+ spec.add_development_dependency 'bundler', '>= 2.1'
36
+ spec.add_development_dependency 'rake', '>= 10.0'
37
+ spec.add_development_dependency 'rspec', '>= 3.0'
38
+ spec.add_development_dependency 'sqlite3', '>= 1.3'
39
+ spec.add_development_dependency 'simplecov', '>= 0.16'
40
40
  end
@@ -1,5 +1,6 @@
1
1
  require 'jet_set/mixin/identity'
2
2
  require 'jet_set/mixin/entity'
3
+ require 'jet_set/validations'
3
4
 
4
5
  module JetSet
5
6
  # A converter of a pure Ruby object to JetSet trackable object.
@@ -22,6 +23,7 @@ module JetSet
22
23
 
23
24
  object.extend(Identity)
24
25
  object.extend(Entity)
26
+ object.extend(Validations)
25
27
  end
26
28
  end
27
29
  end
@@ -15,7 +15,8 @@ module JetSet
15
15
  @container = container
16
16
 
17
17
  @mapping.entity_mappings.values.each do |entity_mapping|
18
- container.register(entity_mapping.type)
18
+ entity_name = "jet_set__#{entity_mapping.type.name.underscore}".to_sym
19
+ container.register(entity_mapping.type, entity_name)
19
20
  end
20
21
  end
21
22
 
@@ -29,6 +30,7 @@ module JetSet
29
30
  # "SELECT u.name AS customer__name from users u"
30
31
  def map(type, row_hash, session, prefix = type.name.underscore)
31
32
  entity_name = type.name.underscore.to_sym
33
+ resolve_name = "jet_set__#{type.name.underscore}".to_sym
32
34
  entity_mapping = @mapping.get(entity_name)
33
35
  row = Row.new(row_hash, entity_mapping.fields, prefix)
34
36
 
@@ -43,7 +45,7 @@ module JetSet
43
45
  end
44
46
  end
45
47
 
46
- object = @container.resolve(entity_name, row.attributes_hash.merge(reference_hash))
48
+ object = @container.resolve(resolve_name, row.attributes_hash.merge(reference_hash))
47
49
  entity = @entity_builder.create(object)
48
50
  entity.load_attributes!(row.attributes)
49
51
 
@@ -77,6 +77,8 @@ module JetSet
77
77
  # Parameters:
78
78
  # +sequel+:: Sequel sequel
79
79
  def flush(sequel)
80
+ validate! if respond_to? :validate!
81
+
80
82
  table_name = self.class.name.underscore.pluralize.to_sym
81
83
  table = sequel[table_name]
82
84
  entity_name = self.class.name.underscore.to_sym
@@ -113,12 +115,14 @@ module JetSet
113
115
  values << value.instance_variable_get('@id')
114
116
  end
115
117
  end
118
+
116
119
  new_id = table.insert(fields, values)
117
120
  @__attributes['@id'] = Attribute.new('@id', new_id)
118
121
  @id = new_id
119
122
  elsif dirty?
120
123
  attributes = {}
121
124
  dirty_attributes.each {|attribute| attributes[attribute.name.sub('@', '')] = instance_variable_get(attribute.name)}
125
+
122
126
  if attributes.keys.length > 0
123
127
  table.where(id: @id).update(attributes)
124
128
  end
@@ -103,9 +103,15 @@ module JetSet
103
103
  dirty_objects = @objects.select {|object| object.dirty?}
104
104
  ordered_objects = @dependency_graph.order(dirty_objects)
105
105
 
106
- if ordered_objects.length > 0
107
- @sequel.transaction do
108
- ordered_objects.each{|obj| obj.flush(@sequel)}
106
+ begin
107
+ if ordered_objects.length > 0
108
+ @sequel.transaction do
109
+ ordered_objects.each{|obj| obj.flush(@sequel)}
110
+ end
111
+ end
112
+ ensure
113
+ @mutex.synchronize do
114
+ @objects = []
109
115
  end
110
116
  end
111
117
  end
@@ -0,0 +1,4 @@
1
+ module JetSet
2
+ class ValidationDefinitionError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,10 @@
1
+ module JetSet
2
+ class ValidationError < StandardError
3
+ attr_reader :invalid_items
4
+
5
+ def initialize(message, invalid_items = {})
6
+ super(message)
7
+ @invalid_items = invalid_items
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,89 @@
1
+ require 'jet_set/validation_error'
2
+
3
+ module JetSet
4
+ # Optional validation decorator. Adds validation logic to pure Ruby objects.
5
+ module Validations
6
+ # The method runs all validations declared in the model
7
+ def validate!
8
+ validations = self.class.class_variable_defined?(:@@validations) ? self.class.class_variable_get(:@@validations) : {}
9
+ attributes = validations.keys
10
+ invalid_items = []
11
+
12
+ attributes.each do |attribute|
13
+ attribute_validations = validations[attribute] || []
14
+
15
+ error = nil
16
+ attribute_validations.each do |validation|
17
+ value = instance_variable_get("@#{attribute}")
18
+ if validation[:func].call(value) == false
19
+ error = validation[:message]
20
+ break
21
+ end
22
+ end
23
+ invalid_items << {"#{attribute}": error} if error
24
+ end
25
+
26
+ raise ValidationError.new("#{self.class.name} is invalid", invalid_items) if invalid_items.length > 0
27
+ end
28
+
29
+ def self.included(base)
30
+ base.extend(ClassMethods)
31
+ end
32
+
33
+ module ClassMethods
34
+ # Adds a validation to an attribute of the entity
35
+ # Parameters:
36
+ # +attribute_name+:: +Symbol+ attribute name
37
+ # +options+ || +message+:: validation options {type, presence, message, custom} or just a message
38
+ # +func+:: boolean proc with a check for validity
39
+ def validate(attribute_name, options, func = nil)
40
+ validations = self.class_variable_defined?(:@@validations) ? self.class_variable_get(:@@validations) : {}
41
+ validations[attribute_name] ||= []
42
+
43
+ if options.is_a?(Hash)
44
+ if options.has_key?(:type)
45
+ validations[attribute_name] << validate_type(options[:type])
46
+ end
47
+
48
+ if options[:presence] == true
49
+ validations[attribute_name] << validate_presence
50
+ end
51
+
52
+ message = options[:message]
53
+ elsif options.is_a?(String)
54
+ message = options
55
+ else
56
+ raise ValidationDefinitionError, "Validation definition of attribute #{attribute_name} is incorrect."
57
+ end
58
+
59
+ func ||= options[:custom]
60
+
61
+ unless func.nil?
62
+ validations[attribute_name] << {func: func, message: message}
63
+ end
64
+
65
+ self.class_variable_set(:@@validations, validations)
66
+ end
67
+
68
+ private
69
+
70
+ def validate_presence
71
+ {message: 'cannot be blank', func: -> (value) {!value.nil? && value != ''}}
72
+ end
73
+
74
+ def validate_type(type)
75
+ unless [:numeric, :string, :boolean].include?(type)
76
+ raise ValidationDefinitionError, "the type should be :numeric, :string or :boolean"
77
+ end
78
+
79
+ checks = {
80
+ numeric: -> (value) {value.is_a?(Numeric)},
81
+ string: -> (value) {value.is_a?(String)},
82
+ boolean: -> (value) {!!value == value}
83
+ }
84
+
85
+ {message: "should be #{type}", func: -> (value) {value.nil? || checks[type].call(value)}}
86
+ end
87
+ end
88
+ end
89
+ end
@@ -1,3 +1,3 @@
1
1
  module JetSet
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/jet_set.rb CHANGED
@@ -3,6 +3,9 @@ require 'sequel'
3
3
  require 'sequel/extensions/inflector'
4
4
  require 'jet_set/environment'
5
5
  require 'jet_set/mapping'
6
+ require 'jet_set/validations'
7
+ require 'jet_set/validation_error'
8
+ require 'jet_set/validation_definition_error'
6
9
  require 'jet_set/version'
7
10
 
8
11
  module JetSet
metadata CHANGED
@@ -1,111 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jet_set
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Kalinkin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-09-03 00:00:00.000000000 Z
11
+ date: 2020-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 5.4.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.4.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: hypo
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.10.0
33
+ version: 1.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.10.0
40
+ version: 1.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '1.15'
47
+ version: '2.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '1.15'
54
+ version: '2.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '10.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '10.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "~>"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '3.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "~>"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: sqlite3
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '1.3'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '1.3'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: simplecov
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0.16'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.16'
111
111
  description: ''
@@ -143,6 +143,9 @@ files:
143
143
  - lib/jet_set/reference.rb
144
144
  - lib/jet_set/row.rb
145
145
  - lib/jet_set/session.rb
146
+ - lib/jet_set/validation_definition_error.rb
147
+ - lib/jet_set/validation_error.rb
148
+ - lib/jet_set/validations.rb
146
149
  - lib/jet_set/version.rb
147
150
  homepage: https://github.com/cylon-v/jet_set
148
151
  licenses:
@@ -164,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
167
  - !ruby/object:Gem::Version
165
168
  version: '0'
166
169
  requirements: []
167
- rubyforge_project:
168
- rubygems_version: 2.7.7
170
+ rubygems_version: 3.1.2
169
171
  signing_key:
170
172
  specification_version: 4
171
173
  summary: JetSet is a microscopic ORM for DDD projects.