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
@@ -0,0 +1,128 @@
1
+ require "test_helper"
2
+
3
+ class TwinSyncTest < MiniTest::Spec
4
+ module Model
5
+ Song = Struct.new(:title, :composer)
6
+ Album = Struct.new(:name, :songs, :artist)
7
+ Artist = Struct.new(:name)
8
+ end
9
+
10
+
11
+ module Twin
12
+ class Album < Disposable::Twin
13
+ feature Setup
14
+ feature Sync
15
+
16
+ property :name
17
+
18
+ collection :songs do
19
+ property :title
20
+
21
+ property :composer do
22
+ property :name
23
+ end
24
+ end
25
+
26
+ property :artist do
27
+ property :name
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ let (:song) { Model::Song.new() }
34
+ let (:composer) { Model::Artist.new(nil) }
35
+ let (:song_with_composer) { Model::Song.new(nil, composer) }
36
+ let (:artist) { Model::Artist.new(nil) }
37
+
38
+
39
+ describe "#sync" do
40
+ let (:album) { Model::Album.new(nil, [song, song_with_composer], artist) }
41
+
42
+ # with populated model.
43
+ it do
44
+ twin = Twin::Album.new(album)
45
+
46
+ # this usually happens in Contract::Validate or in from_* in a representer
47
+ fill_out!(twin)
48
+
49
+ # not written to model, yet.
50
+ album.name.must_equal nil
51
+ album.songs[0].title.must_equal nil
52
+ album.songs[1].title.must_equal nil
53
+ album.songs[1].composer.name.must_equal nil
54
+ album.artist.name.must_equal nil
55
+
56
+ twin.sync
57
+
58
+ album.name.must_equal "Live And Dangerous"
59
+ album.songs[0].must_be_instance_of Model::Song
60
+ album.songs[1].must_be_instance_of Model::Song
61
+ album.songs[0].title.must_equal "Southbound"
62
+ album.songs[1].title.must_equal "The Boys Are Back In Town"
63
+ album.songs[1].composer.must_be_instance_of Model::Artist
64
+ album.songs[1].composer.name.must_equal "Lynott"
65
+ album.artist.must_be_instance_of Model::Artist
66
+ album.artist.name.must_equal "Thin Lizzy"
67
+ end
68
+
69
+ # with empty, not populated model.
70
+ it do
71
+ album = Model::Album.new(nil, [])
72
+ twin = Twin::Album.new(album)
73
+
74
+ # this usually happens in Contract::Validate or in from_* in a representer
75
+ twin.name = "Live And Dangerous"
76
+
77
+ twin.songs.insert(0, song)
78
+ twin.songs.insert(1, song_with_composer)
79
+
80
+ twin.songs[0].title = "Southbound"
81
+ twin.songs[1].title = "The Boys Are Back In Town"
82
+ twin.songs[1].composer.name = "Lynott"
83
+
84
+ # not written to model, yet.
85
+ album.name.must_equal nil
86
+ album.songs.must_equal []
87
+ album.artist.must_equal nil
88
+
89
+ twin.sync # this assigns a new collection via #songs=.
90
+
91
+ album.name.must_equal "Live And Dangerous"
92
+ album.songs[0].title.must_equal "Southbound"
93
+ album.songs[1].title.must_equal "The Boys Are Back In Town"
94
+ album.songs[1].composer.name.must_equal "Lynott"
95
+ end
96
+
97
+ # save with block creates nested_hash and doesn't sync.
98
+ it do
99
+ twin = Twin::Album.new(album)
100
+
101
+ # this usually happens in Contract::Validate or in from_* in a representer
102
+ fill_out!(twin)
103
+
104
+ nested_hash = nil
105
+ twin.sync do |hash|
106
+ nested_hash = hash
107
+ end
108
+
109
+ nested_hash.must_equal({"name"=>"Live And Dangerous", "songs"=>[{"title"=>"Southbound"}, {"title"=>"The Boys Are Back In Town", "composer"=>{"name"=>"Lynott"}}], "artist"=>{"name"=>"Thin Lizzy"}})
110
+
111
+ # nothing written to model.
112
+ album.name.must_equal nil
113
+ album.songs[0].title.must_equal nil
114
+ album.songs[1].title.must_equal nil
115
+ album.songs[1].composer.name.must_equal nil
116
+ album.artist.name.must_equal nil
117
+ end
118
+
119
+
120
+ def fill_out!(twin)
121
+ twin.name = "Live And Dangerous"
122
+ twin.songs[0].title = "Southbound"
123
+ twin.songs[1].title = "The Boys Are Back In Town"
124
+ twin.songs[1].composer.name = "Lynott"
125
+ twin.artist.name = "Thin Lizzy"
126
+ end
127
+ end
128
+ end
@@ -1,10 +1,10 @@
1
1
  require 'test_helper'
2
2
 
3
-
4
3
  class TwinTest < MiniTest::Spec
5
4
  module Model
6
5
  Song = Struct.new(:id, :title, :album)
7
- Album = Struct.new(:id, :name, :songs)
6
+ Album = Struct.new(:id, :name, :songs, :artist)
7
+ Artist = Struct.new(:id)
8
8
  end
9
9
 
10
10
 
@@ -13,16 +13,19 @@ class TwinTest < MiniTest::Spec
13
13
  property :id # DISCUSS: needed for #save.
14
14
  property :name
15
15
  collection :songs, :twin => lambda { |*| Song }
16
-
17
- # model Model::Album
16
+ property :artist, :twin => lambda { |*| Artist }
18
17
  end
19
18
 
20
19
  class Song < Disposable::Twin
21
20
  property :id # DISCUSS: needed for #save.
22
- property :name, :from => :title
21
+ property :title
23
22
  property :album, :twin => Album
23
+ end
24
24
 
25
- # model Model::Song
25
+ class Artist < Disposable::Twin
26
+ property :id
27
+
28
+ include Setup
26
29
  end
27
30
  end
28
31
 
@@ -33,14 +36,14 @@ class TwinTest < MiniTest::Spec
33
36
  twin = Twin::Song.new(song)
34
37
  song.id = 2
35
38
  # :from maps public name
36
- twin.name.must_equal "Broken" # public: #name
37
- twin.id.must_equal 2
39
+ twin.title.must_equal "Broken" # public: #record_name
40
+ twin.id.must_equal 1
38
41
  end
39
42
 
40
- # override property with public name in constructor.
43
+ # allows passing options.
41
44
  it do
42
45
  # override twin's value...
43
- Twin::Song.new(song, :name => "Kenny").name.must_equal "Kenny"
46
+ Twin::Song.new(song, :title => "Kenny").title.must_equal "Kenny"
44
47
 
45
48
  # .. but do not write to the model!
46
49
  song.title.must_equal "Broken"
@@ -49,19 +52,47 @@ class TwinTest < MiniTest::Spec
49
52
 
50
53
  describe "setter" do
51
54
  let (:twin) { Twin::Song.new(song) }
55
+ let (:album) { Model::Album.new(1, "The Stories Are True") }
52
56
 
53
- before do
57
+ it do
54
58
  twin.id = 3
55
- twin.name = "Lucky"
59
+ twin.title = "Lucky"
60
+ twin.album = album # this is a model, not a twin.
61
+
62
+ # updates twin
63
+ twin.id.must_equal 3
64
+ twin.title.must_equal "Lucky"
65
+
66
+ # setter for nested property will twin value.
67
+ twin.album.extend(Disposable::Comparable)
68
+ assert twin.album == Twin::Album.new(album) # FIXME: why does must_equal not call #== ?
69
+
70
+ # setter for nested collection.
71
+
72
+ # DOES NOT update model
73
+ song.id.must_equal 1
74
+ song.title.must_equal "Broken"
56
75
  end
57
76
 
58
- # updates twin
59
- it { twin.id.must_equal 3 }
60
- it { twin.name.must_equal "Lucky" }
77
+ # setters for twin properties return the twin, not the model
78
+ # it do
79
+ # result = twin.album = album
80
+ # result.must_equal twin.album
81
+ # end
82
+ end
83
+
84
+ # FIXME: experimental.
85
+ describe "#to_s" do
86
+ class HitTwin < Disposable::Twin
87
+ include Setup
61
88
 
62
- # DOES NOT update model
63
- it { song.id.must_equal 1 }
64
- it { song.title.must_equal "Broken" }
89
+ property :song do
90
+ end
91
+ end
92
+
93
+ let (:hit) { OpenStruct.new(song: song) }
94
+ it { HitTwin.new(hit).to_s.must_match "#<TwinTest::HitTwin:" }
95
+ it { HitTwin.new(hit).song.to_s.must_match "#<Twin (inline):" }
65
96
  end
66
97
  end
67
98
 
@@ -80,36 +111,6 @@ class OverridingAccessorsTest < TwinTest
80
111
  end
81
112
 
82
113
 
83
- class TwinDecoratorTest < MiniTest::Spec
84
- subject { TwinTest::Twin::Song.representer_class.new(nil) }
85
-
86
- it { subject.twin_names.must_equal [:album] }
87
- end
88
-
89
-
90
- require 'disposable/twin/struct'
91
- class TwinStructTest < MiniTest::Spec
92
- class Song < Disposable::Twin
93
- include Struct
94
- property :number, :default => 1 # FIXME: this should be :default_if_nil so it becomes clear with a model.
95
- option :cool?
96
- end
97
-
98
- # empty hash
99
- it { Song.new({}).number.must_equal 1 }
100
- # model hash
101
- it { Song.new(number: 2).number.must_equal 2 }
102
-
103
- # with hash and options as one hash.
104
- it { Song.new(number: 3, cool?: true).cool?.must_equal true }
105
- it { Song.new(number: 3, cool?: true).number.must_equal 3 }
106
-
107
- # with model hash and options hash separated.
108
- it { Song.new({number: 3}, {cool?: true}).cool?.must_equal true }
109
- it { Song.new({number: 3}, {cool?: true}).number.must_equal 3 }
110
- end
111
-
112
-
113
114
  class TwinAsTest < MiniTest::Spec
114
115
  module Model
115
116
  Song = Struct.new(:title, :album)
@@ -133,84 +134,4 @@ class TwinAsTest < MiniTest::Spec
133
134
  end
134
135
 
135
136
  end
136
-
137
-
138
- class TwinOptionTest < TwinTest
139
- class Song < Disposable::Twin
140
- property :id # DISCUSS: needed for #save.
141
- property :title
142
-
143
- option :preview?
144
- option :highlight?
145
- end
146
-
147
- let (:song) { Model::Song.new(1, "Broken") }
148
- let (:twin) { Song.new(song, :preview? => false) }
149
-
150
-
151
- # properties are read from model.
152
- it { twin.id.must_equal 1 }
153
- it { twin.title.must_equal "Broken" }
154
-
155
- # option is not delegated to model.
156
- it { twin.preview?.must_equal false }
157
- # not passing option means zero.
158
- it { twin.highlight?.must_equal nil }
159
-
160
- # passing both options.
161
- it { Song.new(song, preview?: true, highlight?: false).preview?.must_equal true }
162
- end
163
-
164
-
165
- class TwinBuilderTest < MiniTest::Spec
166
- class Twin < Disposable::Twin
167
- property :id
168
- property :title
169
- option :is_released
170
- end
171
-
172
- describe "without property setup" do
173
- class Host
174
- include Disposable::Twin::Builder
175
-
176
- twin Twin
177
-
178
- def initialize(*args)
179
- @model = build_twin(*args)
180
- end
181
-
182
- attr_reader :model
183
- end
184
-
185
- subject { Host.new(TwinTest::Model::Song.new(1, "Saturday Night"), is_released: true) }
186
-
187
- # model is simply the twin.
188
- it { subject.respond_to?(:title).must_equal false }
189
- it { subject.model.id.must_equal 1 }
190
- it { subject.model.title.must_equal "Saturday Night" }
191
- it { subject.model.is_released.must_equal true }
192
- end
193
-
194
-
195
- describe "without property setup" do
196
- class HostWithReaders
197
- include Disposable::Twin::Builder
198
-
199
- extend Forwardable
200
- twin(Twin) { |dfn| def_delegator :@model, dfn.name }
201
-
202
- def initialize(*args)
203
- @model = build_twin(*args)
204
- end
205
- end
206
-
207
- subject { HostWithReaders.new(TwinTest::Model::Song.new(1, "Saturday Night"), is_released: true) }
208
-
209
- # both twin gets created and reader method defined.
210
- it { subject.id.must_equal 1 }
211
- it { subject.title.must_equal "Saturday Night" }
212
- it { subject.is_released.must_equal true }
213
- end
214
- end
215
-
216
137
  # TODO: test coercion!
@@ -0,0 +1,56 @@
1
+ require 'test_helper'
2
+
3
+ class WriteableTest < MiniTest::Spec
4
+ Credentials = Struct.new(:password, :credit_card) do
5
+ def password=(v)
6
+ raise "don't call me!"
7
+ end
8
+ end
9
+
10
+ CreditCard = Struct.new(:name, :number) do
11
+ def number=(v)
12
+ raise "don't call me!"
13
+ end
14
+ end
15
+
16
+ class PasswordForm < Disposable::Twin
17
+ feature Setup
18
+ feature Sync
19
+
20
+ property :password, writeable: false
21
+
22
+ property :credit_card do
23
+ property :name
24
+ property :number, writeable: false
25
+ end
26
+ end
27
+
28
+ let (:cred) { Credentials.new("secret", CreditCard.new("Jonny", "0987654321")) }
29
+
30
+ let (:twin) { PasswordForm.new(cred) }
31
+
32
+ it {
33
+ twin.password.must_equal "secret"
34
+ twin.credit_card.name.must_equal "Jonny"
35
+ twin.credit_card.number.must_equal "0987654321"
36
+
37
+ # manual setting on the twin works.
38
+ twin.password = "123"
39
+ twin.password.must_equal "123"
40
+
41
+ twin.credit_card.number = "456"
42
+ twin.credit_card.number.must_equal "456"
43
+
44
+ twin.sync
45
+
46
+ cred.inspect.must_equal '#<struct WriteableTest::Credentials password="secret", credit_card=#<struct WriteableTest::CreditCard name="Jonny", number="0987654321">>'
47
+
48
+ # test sync{}.
49
+ hash = {}
50
+ twin.sync do |nested|
51
+ hash = nested
52
+ end
53
+
54
+ hash.must_equal("password"=> "123", "credit_card"=>{"name"=>"Jonny", "number"=>"456"})
55
+ }
56
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: disposable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-06 00:00:00.000000000 Z
11
+ date: 2015-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: representable
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.3
34
+ - - "<="
32
35
  - !ruby/object:Gem::Version
33
- version: '2.0'
36
+ version: 2.3.0
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
39
42
  - !ruby/object:Gem::Version
40
- version: '2.0'
43
+ version: 2.2.3
44
+ - - "<="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.3.0
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: bundler
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -70,17 +76,45 @@ dependencies:
70
76
  name: minitest
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
- - - '='
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: activerecord
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: sqlite3
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
74
108
  - !ruby/object:Gem::Version
75
- version: 5.4.1
109
+ version: '0'
76
110
  type: :development
77
111
  prerelease: false
78
112
  version_requirements: !ruby/object:Gem::Requirement
79
113
  requirements:
80
- - - '='
114
+ - - ">="
81
115
  - !ruby/object:Gem::Version
82
- version: 5.4.1
83
- description: Domain-Oriented Refactoring Framework.
116
+ version: '0'
117
+ description: Decorators on top of your ORM layer.
84
118
  email:
85
119
  - apotonick@gmail.com
86
120
  executables: []
@@ -94,7 +128,6 @@ files:
94
128
  - LICENSE.txt
95
129
  - README.md
96
130
  - Rakefile
97
- - STUFF
98
131
  - database.sqlite3
99
132
  - disposable.gemspec
100
133
  - gemfiles/Gemfile.rails-2.3
@@ -108,23 +141,52 @@ files:
108
141
  - gemfiles/Gemfile.rails-4.1
109
142
  - gemfiles/Gemfile.rails-4.1.lock
110
143
  - lib/disposable.rb
144
+ - lib/disposable/callback.rb
111
145
  - lib/disposable/composition.rb
146
+ - lib/disposable/expose.rb
112
147
  - lib/disposable/twin.rb
113
148
  - lib/disposable/twin/builder.rb
149
+ - lib/disposable/twin/changed.rb
150
+ - lib/disposable/twin/collection.rb
114
151
  - lib/disposable/twin/composition.rb
115
- - lib/disposable/twin/finders.rb
116
- - lib/disposable/twin/new.rb
117
152
  - lib/disposable/twin/option.rb
153
+ - lib/disposable/twin/persisted.rb
154
+ - lib/disposable/twin/property_processor.rb
118
155
  - lib/disposable/twin/representer.rb
119
156
  - lib/disposable/twin/save.rb
120
- - lib/disposable/twin/save_.rb
157
+ - lib/disposable/twin/schema.rb
158
+ - lib/disposable/twin/setup.rb
121
159
  - lib/disposable/twin/struct.rb
160
+ - lib/disposable/twin/sync.rb
122
161
  - lib/disposable/version.rb
123
- - test/composition_test.rb
162
+ - test/api_semantics_test.rb
163
+ - test/callback_group_test.rb
164
+ - test/callbacks_test.rb
165
+ - test/example.rb
166
+ - test/expose_test.rb
167
+ - test/persisted_test.rb
124
168
  - test/test_helper.rb
169
+ - test/twin/benchmarking.rb
170
+ - test/twin/builder_test.rb
171
+ - test/twin/changed_test.rb
172
+ - test/twin/collection_test.rb
125
173
  - test/twin/composition_test.rb
174
+ - test/twin/expose_test.rb
175
+ - test/twin/feature_test.rb
176
+ - test/twin/from_test.rb
177
+ - test/twin/inherit_test.rb
178
+ - test/twin/option_test.rb
179
+ - test/twin/readable_test.rb
180
+ - test/twin/save_test.rb
181
+ - test/twin/schema_test.rb
182
+ - test/twin/setup_test.rb
183
+ - test/twin/skip_unchanged_test.rb
184
+ - test/twin/struct_test.rb
185
+ - test/twin/sync_option_test.rb
186
+ - test/twin/sync_test.rb
126
187
  - test/twin/twin_test.rb
127
- homepage: ''
188
+ - test/twin/writeable_test.rb
189
+ homepage: https://github.com/apotonick/disposable
128
190
  licenses:
129
191
  - MIT
130
192
  metadata: {}
@@ -144,12 +206,36 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
206
  version: '0'
145
207
  requirements: []
146
208
  rubyforge_project:
147
- rubygems_version: 2.2.2
209
+ rubygems_version: 2.4.8
148
210
  signing_key:
149
211
  specification_version: 4
150
- summary: Domain-Oriented Refactoring Framework.
212
+ summary: Decorators on top of your ORM layer with change tracking, collection semantics
213
+ and nesting.
151
214
  test_files:
152
- - test/composition_test.rb
215
+ - test/api_semantics_test.rb
216
+ - test/callback_group_test.rb
217
+ - test/callbacks_test.rb
218
+ - test/example.rb
219
+ - test/expose_test.rb
220
+ - test/persisted_test.rb
153
221
  - test/test_helper.rb
222
+ - test/twin/benchmarking.rb
223
+ - test/twin/builder_test.rb
224
+ - test/twin/changed_test.rb
225
+ - test/twin/collection_test.rb
154
226
  - test/twin/composition_test.rb
227
+ - test/twin/expose_test.rb
228
+ - test/twin/feature_test.rb
229
+ - test/twin/from_test.rb
230
+ - test/twin/inherit_test.rb
231
+ - test/twin/option_test.rb
232
+ - test/twin/readable_test.rb
233
+ - test/twin/save_test.rb
234
+ - test/twin/schema_test.rb
235
+ - test/twin/setup_test.rb
236
+ - test/twin/skip_unchanged_test.rb
237
+ - test/twin/struct_test.rb
238
+ - test/twin/sync_option_test.rb
239
+ - test/twin/sync_test.rb
155
240
  - test/twin/twin_test.rb
241
+ - test/twin/writeable_test.rb