disposable 0.0.9 → 0.1.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -5
  3. data/CHANGES.md +4 -0
  4. data/Gemfile +1 -1
  5. data/README.md +154 -1
  6. data/database.sqlite3 +0 -0
  7. data/disposable.gemspec +7 -7
  8. data/gemfiles/Gemfile.rails-3.0.lock +10 -8
  9. data/gemfiles/Gemfile.rails-3.2.lock +9 -7
  10. data/gemfiles/Gemfile.rails-4.0.lock +9 -7
  11. data/gemfiles/Gemfile.rails-4.1.lock +10 -8
  12. data/lib/disposable.rb +6 -7
  13. data/lib/disposable/callback.rb +174 -0
  14. data/lib/disposable/composition.rb +21 -58
  15. data/lib/disposable/expose.rb +49 -0
  16. data/lib/disposable/twin.rb +85 -38
  17. data/lib/disposable/twin/builder.rb +12 -30
  18. data/lib/disposable/twin/changed.rb +50 -0
  19. data/lib/disposable/twin/collection.rb +95 -0
  20. data/lib/disposable/twin/composition.rb +43 -15
  21. data/lib/disposable/twin/option.rb +1 -1
  22. data/lib/disposable/twin/persisted.rb +20 -0
  23. data/lib/disposable/twin/property_processor.rb +29 -0
  24. data/lib/disposable/twin/representer.rb +42 -14
  25. data/lib/disposable/twin/save.rb +19 -34
  26. data/lib/disposable/twin/schema.rb +31 -0
  27. data/lib/disposable/twin/setup.rb +38 -0
  28. data/lib/disposable/twin/sync.rb +114 -0
  29. data/lib/disposable/version.rb +1 -1
  30. data/test/api_semantics_test.rb +263 -0
  31. data/test/callback_group_test.rb +222 -0
  32. data/test/callbacks_test.rb +450 -0
  33. data/test/example.rb +40 -0
  34. data/test/expose_test.rb +92 -0
  35. data/test/persisted_test.rb +101 -0
  36. data/test/test_helper.rb +64 -0
  37. data/test/twin/benchmarking.rb +33 -0
  38. data/test/twin/builder_test.rb +32 -0
  39. data/test/twin/changed_test.rb +108 -0
  40. data/test/twin/collection_test.rb +223 -0
  41. data/test/twin/composition_test.rb +56 -25
  42. data/test/twin/expose_test.rb +73 -0
  43. data/test/twin/feature_test.rb +61 -0
  44. data/test/twin/from_test.rb +37 -0
  45. data/test/twin/inherit_test.rb +57 -0
  46. data/test/twin/option_test.rb +27 -0
  47. data/test/twin/readable_test.rb +57 -0
  48. data/test/twin/save_test.rb +192 -0
  49. data/test/twin/schema_test.rb +69 -0
  50. data/test/twin/setup_test.rb +139 -0
  51. data/test/twin/skip_unchanged_test.rb +64 -0
  52. data/test/twin/struct_test.rb +168 -0
  53. data/test/twin/sync_option_test.rb +228 -0
  54. data/test/twin/sync_test.rb +128 -0
  55. data/test/twin/twin_test.rb +49 -128
  56. data/test/twin/writeable_test.rb +56 -0
  57. metadata +106 -20
  58. data/STUFF +0 -4
  59. data/lib/disposable/twin/finders.rb +0 -29
  60. data/lib/disposable/twin/new.rb +0 -30
  61. data/lib/disposable/twin/save_.rb +0 -21
  62. data/test/composition_test.rb +0 -102
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 345b091b0b6fb0e6556ab3842a87693dc63398ff
4
- data.tar.gz: f4e6802493f81d2644c61a8bbd676c66e06d1e04
3
+ metadata.gz: c8eb1cfd5be42eb0cf007c4b9d84aee95b7a4275
4
+ data.tar.gz: 76ab1e429806470d007164fd17bdf80d0abeab1a
5
5
  SHA512:
6
- metadata.gz: c80d3106575273ed11dc1334aea93ea1368f4eeb0dc7f47a41cd9143961881e224fb0f40ecaec25ac57dd70e39d4a667e88c1b512f55931b0ba381e14a4241e4
7
- data.tar.gz: 499727d06fe0883a74a66ee752412813be28bb33207692c3a55a564b06f5745f6f27e6620d5436b148fb6075d01b0d3a667747bad251a25b8953222f706c2c27
6
+ metadata.gz: dba5e116162e6fe0f33ff944ef1dbadb9fb8a5208c790fb92c5c0d68091a7edf83e91b9fca49029d8df00db458fb131e1be6c554293fbfcef23730034ada8602
7
+ data.tar.gz: e3d4ac5b51ea771c2444bbdb6b34bcfa05e5b2ff9513050915a470920fd2238e5a639a8ce69225a93cc2a876412759335a0cda068c4b11c0f61aab38ba8603b9
@@ -1,11 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.2.0
3
4
  - 2.1.2
4
5
  - 2.0.0
5
6
  - 1.9.3
6
7
  gemfile:
7
- - gemfiles/Gemfile.rails-4.1
8
- - gemfiles/Gemfile.rails-4.0
9
- - gemfiles/Gemfile.rails-3.2
10
- - gemfiles/Gemfile.rails-3.0
11
- # - gemfiles/Gemfile.rails-2.3
8
+ - Gemfile
data/CHANGES.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.1.0
2
+
3
+ * This is the official first serious release.
4
+
1
5
  # 0.0.9
2
6
 
3
7
  * Rename `:as` to `:from`. Deprecated, this will be removed in 0.1.0.
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in disposable.gemspec
4
4
  gemspec
5
5
 
6
- # gem 'representable', path: '../representable'
6
+ gem 'representable', path: '../representable'
data/README.md CHANGED
@@ -20,6 +20,8 @@ Twins may contain validations, nevertheless, in Trailblazer, validations (or "Co
20
20
 
21
21
  ## Twin
22
22
 
23
+ Twins are only # FIXME % slower than AR alone.
24
+
23
25
  Twins implement light-weight decorators objects with a unified interface. They map objects, hashes, and compositions of objects, along with optional hashes to inject additional options.
24
26
 
25
27
  Let me show you what I mean.
@@ -48,6 +50,8 @@ You need to pass model and the optional options to the twin constructor.
48
50
  twin = Song::Twin.new(song, good?: true)
49
51
  ```
50
52
 
53
+ ++++++ builders
54
+
51
55
  ## Reading
52
56
 
53
57
  This will create a composition object of the actual model and the hash.
@@ -93,8 +97,157 @@ twin = Song::Twin.new(title: "Savior", good?: true)
93
97
 
94
98
  ## Compositions
95
99
 
100
+ ## With Representers
101
+
102
+ they indirect data, the twin's attributes get assigned without writing to the persistence layer, yet.
103
+
104
+ ## With Contracts
105
+
106
+ ## Collections
107
+
108
+ Define collections using `::collection`.
109
+
110
+ ```ruby
111
+ class AlbumTwin < Disposable::Twin
112
+ collection :songs do
113
+
114
+ end
115
+ ```
116
+
117
+ ### API
118
+
119
+ The API is identical to `Array` with the following additions.
120
+
121
+ * `#<<(model)` adds item, wraps it in twin and tracks it via `#added`.
122
+ * `#insert(i, model)`, see `#<<`.
123
+ * `#delete(twin)`, removes twin from collection and tracks via `#deleted`.
124
+ * `#destroy(twin)`, removes twin from collection and tracks via `#deleted` and `#to_destroy` for destruction in `#save`.
125
+
126
+ ### Semantics
127
+
128
+ Include `Twin::Collection::Semantics`.
129
+
130
+ Semantics are extensions to the pure Ruby array behavior and designed to deal with persistence layers like ActiveRecord or ROM.
131
+
132
+ * `#save` will call `destroy` on all models marked for destruction in `to_destroy`. Tracks destruction via `#destroyed`.
133
+
134
+
135
+ ## Imperative Callbacks
136
+
137
+ Note: [Chapter 8 of the Trailblazer](http://leanpub.com/trailblazer) book is dedicated to callbacks and discusses them in great detail.
138
+
139
+ Callbacks use the fact that twins track state changes. This allows to execute callbacks on certain conditions.
140
+
141
+ ```ruby
142
+ Callback.new(twin).on_create { |twin| .. }
143
+ Callback.new(twin.songs).on_add { |twin| .. }
144
+ Callback.new(twin.songs).on_add { |twin| .. }
145
+ ```
146
+
147
+ It works as follows.
148
+
149
+ 1. Twins track state changes, like _"item added to collection (`on_add`)"_ or _"property changed (`on_change`)"_.
150
+ 2. You decide when to invoke one or a group of callbacks. This is why there's no `before_save` and the like anymore.
151
+ 3. You also decide _what_ events to consider by calling the respective events only, like `on_add`.
152
+ 4. The `Callback` will now find out which properties of the twin are affected and exectue your passed code for each of them.
153
+
154
+ This is called _Imperative Callback_ and the opposite of what you've learned from Rails.
155
+
156
+ By inversing the control, we don't need `before_` or `after_`. This is in your hands now and depends on where you invoke your callbacks.
157
+
158
+ ## Events
159
+
160
+ The following events are available in `Callback`.
161
+
162
+ Don't confuse that with event triggering, though! Callbacks are passive, calling an event method means the callback will look for twins that have tracked the respective event (e.g. an twin has `change`d).
163
+
164
+ * `on_update`: Invoked when the underlying model was persisted, yet, at twin initialization and attributes have changed since then.
165
+ * `on_add`: For every twin that has been added to a collection.
166
+ * `on_add(:create)`: For every twin that has been added to a collection and got persisted. This will only pick up collection items after `sync` or `save`.
167
+
168
+ * `on_delete`: For every item that has been deleted from a collection.
169
+ * `on_destroy`: For every item that has been removed from a collection and physically destroyed.
170
+
171
+ * `on_change`: For every item that has changed attributes. When `persisted?` has flippend, this will be triggered, too.
172
+ * `on_change(:email)`: When the scalar field changed.
173
+
174
+
175
+ ## Callback Groups
176
+
177
+ `Callback::Group` simplifies grouping callbacks and allows nesting.
178
+
179
+ ```ruby
180
+ class AfterSave < Disposable::Callback::Group
181
+ on_change :expire_cache!
182
+
183
+ collection :songs do
184
+ on_add :notify_album!
185
+ on_add :reset_song!
186
+ end
187
+
188
+ on_update :rehash_name!, property: :title
189
+
190
+ property :artist do
191
+ on_change :sing!
192
+ end
193
+ end
194
+ ```
195
+
196
+ Calling that group on a twin will invoke all callbacks that apply, in the order they were added.
197
+
198
+ ```ruby
199
+ AfterSave.new(twin).(context: self)
200
+ ```
201
+
202
+ Methods like `:sing!` will be invoked on the `:context` object. Likewise, nested properties will be retrieved by simply calling the getter on the twin, like `twin.songs`.
203
+
204
+ Again, only the events that match will be invoked. If the top level twin hasn't changed, `expire_cache!` won't be invoked. This works by simply using `Callback` under the hood.
205
+
206
+ ## Callback Inheritance
207
+
208
+ You can inherit groups, add and remove callbacks.
209
+
210
+ ```ruby
211
+ class EnhancedAfterSave < AfterSave
212
+ on_change :redo!
213
+
214
+ collection :songs do
215
+ on_add :rewind!
216
+ end
217
+
218
+ remove! :on_change, :expire_cache!
219
+ end
220
+ ```
221
+
222
+ The callbacks will be _appended_ to the existing chain.
223
+
224
+ Instead of appending, you may also refine existing callbacks.
225
+
226
+ ```ruby
227
+ class EnhancedAfterSave < AfterSave
228
+ collection :songs, inherit: true do
229
+ on_delete :rewind!
230
+ end
231
+ end
232
+ ```
233
+
234
+ This will add the `rewind!` callback to the `songs` property, resulting in the following chain.
235
+
236
+ ```ruby
237
+ collection :songs do
238
+ on_add :notify_album!
239
+ on_add :reset_song!
240
+ on_delete :rewind!
241
+ end
242
+ ```
243
+
244
+ ## Builders
245
+
96
246
  ## Overriding Accessors
97
247
 
98
248
  super
99
249
 
100
- ## Used In
250
+ ## Used In
251
+
252
+ * [Reform](https://github.com/apotonick/reform) forms are based on twins and add a little bit of form decoration on top. Every nested form is a twin.
253
+ * [Trailblazer](https://github.com/apotonick/trailblazer) uses twins as decorators and callbacks in operations to structure business logic.
Binary file
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Disposable::VERSION
9
9
  spec.authors = ["Nick Sutterer"]
10
10
  spec.email = ["apotonick@gmail.com"]
11
- spec.description = %q{Domain-Oriented Refactoring Framework.}
12
- spec.summary = %q{Domain-Oriented Refactoring Framework.}
13
- spec.homepage = ""
11
+ spec.description = %q{Decorators on top of your ORM layer.}
12
+ spec.summary = %q{Decorators on top of your ORM layer with change tracking, collection semantics and nesting.}
13
+ spec.homepage = "https://github.com/apotonick/disposable"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -19,12 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "uber"
22
- spec.add_dependency "representable", "~> 2.0"
22
+ spec.add_dependency "representable", ">= 2.2.3", "<= 2.3.0"
23
23
 
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
25
  spec.add_development_dependency "rake"
26
- spec.add_development_dependency "minitest", "5.4.1"
27
- # spec.add_development_dependency "activerecord"
28
- # spec.add_development_dependency "sqlite3"
26
+ spec.add_development_dependency "minitest"
27
+ spec.add_development_dependency "activerecord"
28
+ spec.add_development_dependency "sqlite3"
29
29
  # spec.add_development_dependency "database_cleaner"
30
30
  end
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- disposable (0.0.6)
5
- representable (~> 2.0.3)
4
+ disposable (0.0.8)
5
+ representable (~> 2.0)
6
6
  uber
7
7
 
8
8
  GEM
@@ -35,11 +35,11 @@ GEM
35
35
  abstract (>= 1.0.0)
36
36
  i18n (0.5.4)
37
37
  json (1.8.1)
38
- mini_portile (0.6.0)
38
+ mini_portile (0.6.2)
39
39
  minitest (5.4.1)
40
- multi_json (1.10.1)
41
- nokogiri (1.6.3.1)
42
- mini_portile (= 0.6.0)
40
+ multi_json (1.11.0)
41
+ nokogiri (1.6.6.2)
42
+ mini_portile (~> 0.6.0)
43
43
  rack (1.2.8)
44
44
  rack-mount (0.6.14)
45
45
  rack (>= 1.0.0)
@@ -54,13 +54,14 @@ GEM
54
54
  rake (10.3.2)
55
55
  rdoc (3.12.2)
56
56
  json (~> 1.4)
57
- representable (2.0.4)
57
+ representable (2.1.5)
58
58
  multi_json
59
59
  nokogiri
60
60
  uber (~> 0.0.7)
61
+ sqlite3 (1.3.10)
61
62
  thor (0.14.6)
62
63
  tzinfo (0.3.41)
63
- uber (0.0.8)
64
+ uber (0.0.13)
64
65
 
65
66
  PLATFORMS
66
67
  ruby
@@ -72,3 +73,4 @@ DEPENDENCIES
72
73
  minitest (= 5.4.1)
73
74
  railties (~> 3.0.11)
74
75
  rake
76
+ sqlite3
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- disposable (0.0.6)
5
- representable (~> 2.0.3)
4
+ disposable (0.0.8)
5
+ representable (~> 2.0)
6
6
  uber
7
7
 
8
8
  GEM
@@ -36,11 +36,11 @@ GEM
36
36
  i18n (0.6.11)
37
37
  journey (1.0.4)
38
38
  json (1.8.1)
39
- mini_portile (0.6.0)
39
+ mini_portile (0.6.2)
40
40
  minitest (5.4.1)
41
41
  multi_json (1.10.1)
42
- nokogiri (1.6.3.1)
43
- mini_portile (= 0.6.0)
42
+ nokogiri (1.6.6.2)
43
+ mini_portile (~> 0.6.0)
44
44
  rack (1.4.5)
45
45
  rack-cache (1.2)
46
46
  rack (>= 0.4)
@@ -58,7 +58,7 @@ GEM
58
58
  rake (10.3.2)
59
59
  rdoc (3.12.2)
60
60
  json (~> 1.4)
61
- representable (2.0.4)
61
+ representable (2.1.5)
62
62
  multi_json
63
63
  nokogiri
64
64
  uber (~> 0.0.7)
@@ -67,10 +67,11 @@ GEM
67
67
  multi_json (~> 1.0)
68
68
  rack (~> 1.0)
69
69
  tilt (~> 1.1, != 1.3.0)
70
+ sqlite3 (1.3.10)
70
71
  thor (0.19.1)
71
72
  tilt (1.4.1)
72
73
  tzinfo (0.3.41)
73
- uber (0.0.8)
74
+ uber (0.0.13)
74
75
 
75
76
  PLATFORMS
76
77
  ruby
@@ -82,3 +83,4 @@ DEPENDENCIES
82
83
  minitest (= 5.4.1)
83
84
  railties (~> 3.2.0)
84
85
  rake
86
+ sqlite3
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- disposable (0.0.6)
5
- representable (~> 2.0.3)
4
+ disposable (0.0.8)
5
+ representable (~> 2.0)
6
6
  uber
7
7
 
8
8
  GEM
@@ -33,11 +33,11 @@ GEM
33
33
  builder (3.1.4)
34
34
  erubis (2.7.0)
35
35
  i18n (0.6.11)
36
- mini_portile (0.6.0)
36
+ mini_portile (0.6.2)
37
37
  minitest (4.2.0)
38
38
  multi_json (1.10.1)
39
- nokogiri (1.6.3.1)
40
- mini_portile (= 0.6.0)
39
+ nokogiri (1.6.6.2)
40
+ mini_portile (~> 0.6.0)
41
41
  rack (1.5.2)
42
42
  rack-test (0.6.2)
43
43
  rack (>= 1.0)
@@ -47,14 +47,15 @@ GEM
47
47
  rake (>= 0.8.7)
48
48
  thor (>= 0.18.1, < 2.0)
49
49
  rake (10.3.2)
50
- representable (2.0.4)
50
+ representable (2.1.5)
51
51
  multi_json
52
52
  nokogiri
53
53
  uber (~> 0.0.7)
54
+ sqlite3 (1.3.10)
54
55
  thor (0.19.1)
55
56
  thread_safe (0.3.4)
56
57
  tzinfo (0.3.41)
57
- uber (0.0.8)
58
+ uber (0.0.13)
58
59
 
59
60
  PLATFORMS
60
61
  ruby
@@ -66,3 +67,4 @@ DEPENDENCIES
66
67
  minitest (~> 4.2.0)
67
68
  railties (~> 4.0.0)
68
69
  rake
70
+ sqlite3
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: ../
3
3
  specs:
4
- disposable (0.0.6)
5
- representable (~> 2.0.3)
4
+ disposable (0.0.8)
5
+ representable (~> 2.0)
6
6
  uber
7
7
 
8
8
  GEM
@@ -35,11 +35,11 @@ GEM
35
35
  erubis (2.7.0)
36
36
  i18n (0.6.11)
37
37
  json (1.8.1)
38
- mini_portile (0.6.0)
38
+ mini_portile (0.6.2)
39
39
  minitest (5.4.1)
40
- multi_json (1.10.1)
41
- nokogiri (1.6.3.1)
42
- mini_portile (= 0.6.0)
40
+ multi_json (1.11.0)
41
+ nokogiri (1.6.6.2)
42
+ mini_portile (~> 0.6.0)
43
43
  rack (1.5.2)
44
44
  rack-test (0.6.2)
45
45
  rack (>= 1.0)
@@ -49,15 +49,16 @@ GEM
49
49
  rake (>= 0.8.7)
50
50
  thor (>= 0.18.1, < 2.0)
51
51
  rake (10.3.2)
52
- representable (2.0.4)
52
+ representable (2.1.5)
53
53
  multi_json
54
54
  nokogiri
55
55
  uber (~> 0.0.7)
56
+ sqlite3 (1.3.10)
56
57
  thor (0.19.1)
57
58
  thread_safe (0.3.4)
58
59
  tzinfo (1.2.2)
59
60
  thread_safe (~> 0.1)
60
- uber (0.0.8)
61
+ uber (0.0.13)
61
62
 
62
63
  PLATFORMS
63
64
  ruby
@@ -69,3 +70,4 @@ DEPENDENCIES
69
70
  minitest (= 5.4.1)
70
71
  railties (~> 4.1.0)
71
72
  rake
73
+ sqlite3