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 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