disposable 0.2.3 → 0.2.4

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
2
  SHA1:
3
- metadata.gz: abc930b3fee06aab580e516948996aff0e05ec45
4
- data.tar.gz: 4902d414b02e4fc94ad30ad360d99671f099b3fc
3
+ metadata.gz: 9b938f2252d58d5182b64346aa82ec1f16567837
4
+ data.tar.gz: bf6f9fcbd1cb340498f2764036b3dc9d738b4a28
5
5
  SHA512:
6
- metadata.gz: aa8ef4dae7775e779bb22de5bff27b7d503a7c9af92fef70b43ff3f679baecdbbacf9a53805882ee8368e7780adb9e5ac75a0f13bf9a4f70202f22ee75b09e1e
7
- data.tar.gz: c6c45cfcbd947aeb2a7c2cab6ea9b6d7157a2daba38e59c756f73dcb8ce7a534391c6355207e404aeb11dd89999db6703c12ba4f409f231c4668cf922d8956f0
6
+ metadata.gz: d974e955ee6f3d0d39c6c83868c67c2c817a41ac5771e913e4311ec9b3933a6ce17e75dfe4493fc1fe6d15e91aba36e054a68305fde740d833520c2bda305ce2
7
+ data.tar.gz: 41485aeda7974a7986c350b47c7bfec88cf4de551929c68931b8fdfe6af17e974a25df74e28427dae20e49fdea8789eb77c623f69428f8b07981091aab7ac316
data/CHANGES.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.2.4
2
+
3
+ * Add `Twin::Parent` to access a nested's parent twin.
4
+ * Introduce `Twin#build_for` and `Twin#build_twin`. This is a private API change.
5
+
1
6
  # 0.2.3
2
7
 
3
8
  * Add `Collection#find_by` for easier traversal/querying of twin collections: `album.songs.find_by(id: 1)`.
data/README.md CHANGED
@@ -581,9 +581,51 @@ Properties can have various access settings.
581
581
  * `writeable: false` won't write to model in `Sync`.
582
582
  * `virtual: true` is both settings above combined.
583
583
 
584
+ ## Options
585
+
586
+ To inject context data into a twin that is not part of any model, you can simply use `:virtual` properties.
587
+
588
+ ```ruby
589
+ class AlbumTwin < Disposable::Twin
590
+ property :title
591
+ property :current_user, virtual: true
592
+ end
593
+ ```
594
+
595
+ You can now pass the `current_user` as an option into the constructor and then access it via the reader.
596
+
597
+ ```ruby
598
+ twin = AlbumTwin.new(album, current_user: User.find(1))
599
+ twin.current_user #=> <User id:1>
600
+ ```
601
+
602
+ ## Parent
603
+
604
+ By using the `Parent` feature you can access the parent twin of a nested one.
605
+
606
+ ```ruby
607
+ class AlbumTwin < Disposable::Twin
608
+ feature Parent
609
+
610
+ property :artist do
611
+ property :name
612
+ end
613
+ end
614
+ ```
615
+
616
+ Use `parent` to grab the nested's container twin.
617
+
618
+ ```ruby
619
+ twin = AlbumTwin.new(Album.new(artist: Artist.new))
620
+
621
+ twin.artist.parent #=> twin
622
+ ```
623
+
624
+ Note that this will internally add a `parent` property.
625
+
584
626
  ## Builders
585
627
 
586
628
  ## Used In
587
629
 
588
630
  * [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.
589
- * [Trailblazer](https://github.com/apotonick/trailblazer) uses twins as decorators and callbacks in operations to structure business logic.
631
+ * [Trailblazer](https://github.com/apotonick/trailblazer) uses twins as decorators and callbacks in operations to structure business logic.
@@ -53,11 +53,13 @@ module Disposable
53
53
  property(name, options.merge(collection: true), &block)
54
54
  end
55
55
 
56
+ # TODO: remove.
56
57
  def from_collection(collection)
57
58
  collection.collect { |model| new(model) }
58
59
  end
59
60
 
60
61
  private
62
+
61
63
  def create_accessors(name, definition)
62
64
  mod = Module.new do
63
65
  define_method(name) { @fields[name.to_s] }
@@ -75,9 +77,7 @@ module Disposable
75
77
  private
76
78
  # assumption: collections are always initialized from Setup since we assume an empty [] for "nil"/uninitialized collections.
77
79
  def write_property(name, value, dfn)
78
- if dfn[:nested] and value
79
- value = dfn[:collection] ? wrap_collection(dfn, value) : wrap_scalar(dfn, value)
80
- end
80
+ value = build_for(dfn, value) if dfn[:nested] and value
81
81
 
82
82
  field_write(name, value)
83
83
  end
@@ -92,27 +92,33 @@ module Disposable
92
92
  @fields[name.to_s]
93
93
  end
94
94
 
95
- def wrap_scalar(dfn, value)
96
- Twinner.new(dfn).(value)
95
+ # Build a twin or a Twin::Collection for the value (which is a model or array of).
96
+ def build_for(dfn, *args)
97
+ dfn[:collection] ? build_collection(dfn, *args) : build_twin(dfn, *args)
97
98
  end
98
99
 
99
- def wrap_collection(dfn, value)
100
- Collection.for_models(Twinner.new(dfn), value)
100
+ def build_twin(dfn, *args)
101
+ dfn[:nested].new(*args) # Twin.new(model, options={})
102
+ end
103
+
104
+ def build_collection(dfn, *args)
105
+ Collection.for_models(Twinner.new(self, dfn), *args)
101
106
  end
102
107
  end
103
108
  include Accessors
104
109
 
110
+ # TODO: make this a function so it's faster at run-time.
105
111
  class Twinner
106
- def initialize(dfn)
107
- @dfn = dfn
112
+ def initialize(twin, dfn)
113
+ @twin = twin
114
+ @dfn = dfn
108
115
  end
109
116
 
110
- def call(value)
111
- @dfn[:nested].new(value)
117
+ def call(*args)
118
+ @twin.send(:build_twin, @dfn, *args)
112
119
  end
113
120
  end
114
121
 
115
-
116
122
  private
117
123
  module ModelReaders
118
124
  attr_reader :model # #model is a private concept.
@@ -3,8 +3,8 @@ module Disposable
3
3
  # Provides collection semantics like add, delete, and more for twin collections.
4
4
  # Tracks additions and deletions in #added and #deleted.
5
5
  class Collection < Array
6
- def self.for_models(twinner, models)
7
- new(twinner, models.collect { |model| twinner.(model) })
6
+ def self.for_models(twinner, models, *options)
7
+ new(twinner, models.collect { |model| twinner.(model, *options) })
8
8
  end
9
9
 
10
10
  def initialize(twinner, items)
@@ -0,0 +1,10 @@
1
+ module Disposable::Twin::Parent
2
+ def self.included(includer)
3
+ includer.property(:parent, virtual: true)
4
+ end
5
+
6
+ # FIXME: for collections, this will merge options for every element.
7
+ def build_twin(dfn, value, options={})
8
+ super(dfn, value, options.merge(parent: self))
9
+ end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module Disposable
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -1,26 +1,21 @@
1
1
  require "disposable/twin"
2
- require 'reform'
3
2
  require 'ostruct'
4
3
  require 'benchmark'
5
4
 
6
- class BandForm < Reform::Form
7
- property :name, validates: {presence: true}
5
+ class Band < Disposable::Twin
6
+ property :name
8
7
 
9
8
  collection :songs do
10
- property :title, validates: {presence: true}
9
+ property :title
11
10
  end
12
11
  end
13
12
 
14
- songs = 50.times.collect { OpenStruct.new(title: "Be Stag") }
15
- band = OpenStruct.new(name: "Teenage Bottlerock", songs: songs)
16
-
17
- songs_params = 50.times.collect { {title: "Commando"} }
13
+ songs = 50.times.collect { Struct.new(:title).new("Be Stag") }
14
+ band = Struct.new(:name, :songs).new("Teenage Bottlerock", songs)
18
15
 
19
16
  time = Benchmark.measure do
20
- 100.times.each do
21
- form = BandForm.new(band)
22
- form.validate("name" => "Ramones", "songs" => songs_params)
23
- form.save
17
+ 1000.times do
18
+ Band.new(band)
24
19
  end
25
20
  end
26
21
 
@@ -30,4 +25,8 @@ puts time
30
25
  # 4.200000
31
26
  # 20%
32
27
  # with setup and new(fields).from_object(twin) instead of Fields.new(to_hash)
33
- # 3.680000 0.000000 3.680000 ( 3.685796)
28
+ # 3.680000 0.000000 3.680000 ( 3.685796)
29
+
30
+
31
+ # 06/01
32
+ # 0.300000 0.000000 0.300000 ( 0.298956)
@@ -218,7 +218,8 @@ class CollectionUnitTest < MiniTest::Spec
218
218
  Album = Struct.new(:id, :name, :songs, :artist)
219
219
  end
220
220
 
221
- let(:collection) { Disposable::Twin::Collection.new(Disposable::Twin::Twinner.new(Twin::Song.definitions.get(:album)), []) }
221
+ # THIS is why private tests suck!
222
+ let(:collection) { Disposable::Twin::Collection.new(Disposable::Twin::Twinner.new(Twin::Song.new(OpenStruct.new), Twin::Song.definitions.get(:album)), []) }
222
223
 
223
224
  # #insert(index, model)
224
225
  it do
@@ -1,12 +1,20 @@
1
1
  # require "test_helper"
2
2
 
3
- # class TwinOptionTest < TwinTest
3
+ # class TwinOptionTest < Minitest::Spec
4
+ # module Model
5
+ # Song = Struct.new(:id, :title, :album)
6
+ # end
7
+
4
8
  # class Song < Disposable::Twin
5
- # property :id # DISCUSS: needed for #save.
9
+ # property :id
6
10
  # property :title
7
11
 
12
+ # def self.option(name, options={})
13
+ # property(name, options.merge(virtual: true))
14
+ # end
15
+
8
16
  # option :preview?
9
- # option :highlight?
17
+ # option :current_user
10
18
  # end
11
19
 
12
20
  # let (:song) { Model::Song.new(1, "Broken") }
@@ -20,8 +28,12 @@
20
28
  # # option is not delegated to model.
21
29
  # it { twin.preview?.must_equal false }
22
30
  # # not passing option means zero.
23
- # it { twin.highlight?.must_equal nil }
31
+ # it { twin.current_user.must_equal nil }
24
32
 
25
- # # passing both options.
26
- # it { Song.new(song, preview?: true, highlight?: false).preview?.must_equal true }
33
+ # describe "passing all options" do
34
+ # let (:twin) { Song.new(song, :preview? => false, current_user: Object) }
35
+
36
+ # it { twin.preview?.must_equal false }
37
+ # it { twin.current_user.must_equal Object }
38
+ # end
27
39
  # end
@@ -0,0 +1,41 @@
1
+ require "test_helper"
2
+ require "disposable/twin/parent.rb"
3
+
4
+ class TwinParentTest < MiniTest::Spec
5
+ module Model
6
+ Album = Struct.new(:id, :artist, :songs)
7
+ Artist = Struct.new(:name)
8
+ Song = Struct.new(:title, :composer)
9
+ end
10
+
11
+ class Album < Disposable::Twin
12
+ feature Parent
13
+
14
+ property :id
15
+
16
+ property :artist do
17
+ property :name
18
+ end
19
+
20
+ collection :songs do
21
+ property :title
22
+ property :composer do
23
+ property :name
24
+ end
25
+ end
26
+ end
27
+
28
+ let (:album) { Album.new(Model::Album.new(1, Model::Artist.new("Helloween"), [Model::Song.new("I'm Alive", Model::Artist.new("Kai Hansen"))])) }
29
+
30
+ it { album.parent.must_equal nil }
31
+ it { album.artist.parent.must_equal album }
32
+ it { album.songs[0].parent.must_equal album }
33
+ it { album.songs[0].composer.parent.must_equal album.songs[0] }
34
+
35
+ describe "Collection#append" do
36
+ it do
37
+ album.songs.append(song = Model::Song.new)
38
+ album.songs[1].parent.must_equal album
39
+ end
40
+ end
41
+ 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.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-27 00:00:00.000000000 Z
11
+ date: 2016-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -175,6 +175,7 @@ files:
175
175
  - lib/disposable/twin/composition.rb
176
176
  - lib/disposable/twin/default.rb
177
177
  - lib/disposable/twin/definitions.rb
178
+ - lib/disposable/twin/parent.rb
178
179
  - lib/disposable/twin/persisted.rb
179
180
  - lib/disposable/twin/property_processor.rb
180
181
  - lib/disposable/twin/save.rb
@@ -204,6 +205,7 @@ files:
204
205
  - test/twin/from_test.rb
205
206
  - test/twin/inherit_test.rb
206
207
  - test/twin/option_test.rb
208
+ - test/twin/parent_test.rb
207
209
  - test/twin/process_inline_test.rb
208
210
  - test/twin/readable_test.rb
209
211
  - test/twin/save_test.rb
@@ -263,6 +265,7 @@ test_files:
263
265
  - test/twin/from_test.rb
264
266
  - test/twin/inherit_test.rb
265
267
  - test/twin/option_test.rb
268
+ - test/twin/parent_test.rb
266
269
  - test/twin/process_inline_test.rb
267
270
  - test/twin/readable_test.rb
268
271
  - test/twin/save_test.rb