jet_set 0.3.2 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9d9c31fea3ab85535b7334a1bcfea9f5a15577f6
4
- data.tar.gz: 9dff8880d96dc20c7674a64e66553ac104da18be
2
+ SHA256:
3
+ metadata.gz: 4c2b21527242c8574e604c1ece2b26ec71d59c7712b76cdfa9e88fdc6a47265b
4
+ data.tar.gz: bdcba0c32be1c7db5a0b63f206dd7c5e354c30a9af14eb1b0b204a55ea4ce349
5
5
  SHA512:
6
- metadata.gz: 23b7ed643ec6ad827879e7d6f2e1e9d0f038a78cd7051483de3c85f6ea1f32d02361d098ecbeab6648375b5d206367028bdd16310ca8a11bd76962ece12b0258
7
- data.tar.gz: c59832f80bbf2996665384963be940e81ae9bc26a68a28f6d5bb212e28c1899710c544cfb5eeff48564e5a9a7458453eddaa475708d5ad11809a765111bb8964
6
+ metadata.gz: b6e155d9e2c20157beb3689764a42f8618159fb443b11c41d2ae81a5405fc3fadb55e31a562fcbf58fb0e06a78bf8611ea10eb36378c2b14e4c3e59c6e5b409b
7
+ data.tar.gz: 076a5aa11cb87fa6773faca5d3b92e218e5e939a6808d1825486c8cc1c8428cbeb1b480fe152f69a3e6f09b9f5431b4ebbec70873c567a44e91c6439f8150120
data/Gemfile CHANGED
@@ -2,4 +2,3 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
-
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/jet_set`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Write usage instructions here
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.9.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'
@@ -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
- entity.set_reference! reference_name, map(type, row_hash, session, reference_name)
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
@@ -6,7 +6,7 @@ module JetSet
6
6
  module Entity
7
7
  # Loads the entity attributes.
8
8
  # Parameters:
9
- # +attributes+:: a hash of attributes in format :field => :value
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
@@ -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
- to_attach.each do |object|
91
- obj = object.kind_of?(Entity) ? object : @entity_builder.create(object)
92
- @objects << obj
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
 
@@ -1,3 +1,3 @@
1
1
  module JetSet
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.0"
3
3
  end
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.3.2
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-05-06 00:00:00.000000000 Z
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.9.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.9.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.6.12
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.
data/test.rb DELETED
@@ -1,16 +0,0 @@
1
- class Test
2
- def initialize
3
- @method = 0
4
- end
5
-
6
- def calc
7
- @method = 1
8
- puts @method
9
- end
10
- end
11
-
12
- Test.define_method '@method=' do |value|
13
- @method = value + 5
14
- end
15
- test = Test.new
16
- test.calc