disposable 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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