jet_set 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +0 -1
- data/README.md +120 -5
- data/jet_set.gemspec +1 -2
- data/lib/jet_set/mapper.rb +10 -4
- data/lib/jet_set/mixin/entity.rb +1 -1
- data/lib/jet_set/row.rb +6 -1
- data/lib/jet_set/session.rb +6 -3
- data/lib/jet_set/version.rb +1 -1
- metadata +5 -6
- data/test.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4c2b21527242c8574e604c1ece2b26ec71d59c7712b76cdfa9e88fdc6a47265b
|
4
|
+
data.tar.gz: bdcba0c32be1c7db5a0b63f206dd7c5e354c30a9af14eb1b0b204a55ea4ce349
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6e155d9e2c20157beb3689764a42f8618159fb443b11c41d2ae81a5405fc3fadb55e31a562fcbf58fb0e06a78bf8611ea10eb36378c2b14e4c3e59c6e5b409b
|
7
|
+
data.tar.gz: 076a5aa11cb87fa6773faca5d3b92e218e5e939a6808d1825486c8cc1c8428cbeb1b480fe152f69a3e6f09b9f5431b4ebbec70873c567a44e91c6439f8150120
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# JetSet ![Build Status](https://travis-ci.org/cylon-v/jet_set.svg?branch=master)
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
JetSet is a data mapping framework for domain-driven developers who think that SQL is the best tool for data querying.
|
4
|
+
JetSet is built on top of [Sequel](https://github.com/jeremyevans/sequel) ORM and it's just an abstraction for making
|
5
|
+
the persistence of mapped objects invisible.
|
7
6
|
|
8
7
|
## Installation
|
9
8
|
|
@@ -23,7 +22,123 @@ Or install it yourself as:
|
|
23
22
|
|
24
23
|
## Usage
|
25
24
|
|
26
|
-
|
25
|
+
### Initialization
|
26
|
+
Open DB connection, see [Sequel docs](https://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html):
|
27
|
+
```ruby
|
28
|
+
@connection = Sequel.connect('sqlite:/') # you can connect to any DB supported by Sequel
|
29
|
+
```
|
30
|
+
|
31
|
+
Create a mapping of your model, a details described [here]:
|
32
|
+
```ruby
|
33
|
+
class Mapping
|
34
|
+
def self.load_mapping
|
35
|
+
JetSet::map do
|
36
|
+
entity User do
|
37
|
+
field :first_name # reqular field
|
38
|
+
collection :invoices, type: Invoice # "has many" association
|
39
|
+
reference :plan, type: Plan, weak: true # "belongs to" association
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Init JetSet environment on start of your application:
|
47
|
+
```ruby
|
48
|
+
JetSet::init(Mapping.load_mapping, @container)
|
49
|
+
```
|
50
|
+
|
51
|
+
Open JetSet session:
|
52
|
+
```ruby
|
53
|
+
@jet_set = JetSet::open_session(@connection)
|
54
|
+
```
|
55
|
+
For web-applications it's reasonable to bind JetSet session to request lifetime -
|
56
|
+
all modification operations in an MVC action can represent a ["Unit of Work"](https://martinfowler.com/eaaCatalog/unitOfWork.html).
|
57
|
+
|
58
|
+
### Object model
|
59
|
+
Using JetSet you can wrap an application domain model and purely implement "Persistence Ignorance" approach.
|
60
|
+
The model objects are pure Ruby objects without any noisy stuff like annotations, inline mapping, etc:
|
61
|
+
```ruby
|
62
|
+
class User
|
63
|
+
attr_reader :invoices
|
64
|
+
|
65
|
+
def initialize(attrs = {})
|
66
|
+
@first_name = attrs[:first_name]
|
67
|
+
@last_name = attrs[:last_name]
|
68
|
+
@invoices = []
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_invoice(invoice)
|
72
|
+
@invoices << invoice
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Invoice
|
77
|
+
attr_reader :created_at, :amount
|
78
|
+
|
79
|
+
def initialize(attrs = {})
|
80
|
+
@created_at = DateTime.now
|
81
|
+
@amount = attrs[:amount] || 0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
### Object model tracking and saving
|
87
|
+
Create an objects which is described in the mapping:
|
88
|
+
```ruby
|
89
|
+
user = User.new(first_name: 'Ivan', last_name: 'Ivanov')
|
90
|
+
invoice = Invoice.new(created_at: DateTime.now, user: user, amount: 100.0)
|
91
|
+
```
|
92
|
+
|
93
|
+
Attach them to the session:
|
94
|
+
```ruby
|
95
|
+
@session.attach(invoice, user)
|
96
|
+
```
|
97
|
+
It makes the objects tracked by JetSet.
|
98
|
+
|
99
|
+
Finalize the session:
|
100
|
+
```ruby
|
101
|
+
@session.finalize
|
102
|
+
```
|
103
|
+
It saves all added/changed objects to the database.
|
104
|
+
|
105
|
+
### Object model loading
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
user_query = <<~SQL
|
109
|
+
SELECT
|
110
|
+
u.* AS ENTITY user
|
111
|
+
FROM users u
|
112
|
+
LIMIT 1
|
113
|
+
SQL
|
114
|
+
|
115
|
+
invoices_sql = <<~SQL
|
116
|
+
SELECT
|
117
|
+
i.* AS ENTITY invoice
|
118
|
+
WHERE i.user_id = :user_id
|
119
|
+
SQL
|
120
|
+
|
121
|
+
customer = @session.fetch(User, user_query) do |user|
|
122
|
+
preload(user, :invoices, invoices_sql, user_id: user.id)
|
123
|
+
end
|
124
|
+
```
|
125
|
+
All loaded objects are already attached to the session and you can perform a changes which will be saved after the session finalization:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
customer.invoices[0].apply # changes invoice state
|
129
|
+
@session.finalize
|
130
|
+
```
|
131
|
+
|
132
|
+
Do not load your object model just for drawing a views. For showing a results just use Sequel without any object mappings:
|
133
|
+
```ruby
|
134
|
+
result = @connection[:user].where(role: 'admin').to_a
|
135
|
+
json = JSON.generate(data: result)
|
136
|
+
```
|
137
|
+
In other words, following [CQS](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) approach you can
|
138
|
+
load your model for a command but not for a query.
|
139
|
+
|
140
|
+
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].
|
27
142
|
|
28
143
|
## Development
|
29
144
|
|
data/jet_set.gemspec
CHANGED
@@ -31,8 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.require_paths = ['lib']
|
32
32
|
|
33
33
|
spec.add_dependency 'sequel', '~> 5.4.0'
|
34
|
-
spec.add_dependency 'hypo', '~> 0.
|
35
|
-
|
34
|
+
spec.add_dependency 'hypo', '~> 0.10.0'
|
36
35
|
spec.add_development_dependency 'bundler', '~> 1.15'
|
37
36
|
spec.add_development_dependency 'rake', '~> 10.0'
|
38
37
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
data/lib/jet_set/mapper.rb
CHANGED
@@ -31,20 +31,26 @@ module JetSet
|
|
31
31
|
entity_name = type.name.underscore.to_sym
|
32
32
|
entity_mapping = @mapping.get(entity_name)
|
33
33
|
row = Row.new(row_hash, entity_mapping.fields, prefix)
|
34
|
-
object = @container.resolve(entity_name)
|
35
|
-
entity = @entity_builder.create(object)
|
36
|
-
entity.load_attributes!(row.attributes)
|
37
34
|
|
35
|
+
reference_hash = {}
|
38
36
|
row.reference_names.each do |reference_name|
|
39
37
|
if entity_mapping.references.key? reference_name.to_sym
|
40
38
|
reference_id_name = reference_name + '__id'
|
41
39
|
unless row_hash[reference_id_name.to_sym].nil?
|
42
40
|
type = entity_mapping.references[reference_name.to_sym].type
|
43
|
-
|
41
|
+
reference_hash[reference_name.to_sym] = map(type, row_hash, session, reference_name)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
46
|
+
object = @container.resolve(entity_name, row.attributes_hash.merge(reference_hash))
|
47
|
+
entity = @entity_builder.create(object)
|
48
|
+
entity.load_attributes!(row.attributes)
|
49
|
+
|
50
|
+
reference_hash.each do |key, value|
|
51
|
+
entity.set_reference! key.to_s, value
|
52
|
+
end
|
53
|
+
|
48
54
|
session.attach(entity)
|
49
55
|
entity
|
50
56
|
end
|
data/lib/jet_set/mixin/entity.rb
CHANGED
@@ -6,7 +6,7 @@ module JetSet
|
|
6
6
|
module Entity
|
7
7
|
# Loads the entity attributes.
|
8
8
|
# Parameters:
|
9
|
-
# +attributes+::
|
9
|
+
# +attributes+:: an array of key-pairs in format :field => :value
|
10
10
|
def load_attributes!(attributes)
|
11
11
|
attributes.each do |attribute|
|
12
12
|
name = "@#{attribute[:field]}"
|
data/lib/jet_set/row.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module JetSet
|
2
2
|
# A container for fields/references extraction logic
|
3
3
|
class Row
|
4
|
-
attr_reader :attributes, :reference_names
|
4
|
+
attr_reader :attributes, :attributes_hash, :reference_names
|
5
5
|
|
6
6
|
def initialize(row_hash, entity_fields, prefix)
|
7
7
|
keys = row_hash.keys.map {|key| key.to_s}
|
@@ -10,6 +10,11 @@ module JetSet
|
|
10
10
|
.select {|key| entity_fields.include? key.sub(prefix + '__', '')}
|
11
11
|
.map {|key| {field: key.sub(prefix + '__', ''), value: row_hash[key.to_sym]}}
|
12
12
|
|
13
|
+
@attributes_hash = {}
|
14
|
+
@attributes.each do |attr|
|
15
|
+
@attributes_hash[attr[:field].to_sym] = attr[:value]
|
16
|
+
end
|
17
|
+
|
13
18
|
@reference_names = keys.select {|key| !key.start_with?(prefix) && key.include?('__')}
|
14
19
|
.map {|key| key.split('__')[0]}
|
15
20
|
.uniq
|
data/lib/jet_set/session.rb
CHANGED
@@ -14,6 +14,7 @@ module JetSet
|
|
14
14
|
@query_parser = query_parser
|
15
15
|
@entity_builder = entity_builder
|
16
16
|
@dependency_graph = dependency_graph
|
17
|
+
@mutex = Mutex.new
|
17
18
|
end
|
18
19
|
|
19
20
|
# Fetches root entity using a result of +execute+ method.
|
@@ -87,9 +88,11 @@ module JetSet
|
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
@mutex.synchronize do
|
92
|
+
to_attach.each do |object|
|
93
|
+
obj = object.kind_of?(Entity) ? object : @entity_builder.create(object)
|
94
|
+
@objects << obj
|
95
|
+
end
|
93
96
|
end
|
94
97
|
end
|
95
98
|
|
data/lib/jet_set/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jet_set
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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-
|
11
|
+
date: 2018-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.10.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.
|
40
|
+
version: 0.10.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,7 +144,6 @@ files:
|
|
144
144
|
- lib/jet_set/row.rb
|
145
145
|
- lib/jet_set/session.rb
|
146
146
|
- lib/jet_set/version.rb
|
147
|
-
- test.rb
|
148
147
|
homepage: https://github.com/cylon-v/jet_set
|
149
148
|
licenses:
|
150
149
|
- MIT
|
@@ -166,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
165
|
version: '0'
|
167
166
|
requirements: []
|
168
167
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.
|
168
|
+
rubygems_version: 2.7.7
|
170
169
|
signing_key:
|
171
170
|
specification_version: 4
|
172
171
|
summary: JetSet is a microscopic ORM for DDD projects.
|