disposable 0.1.15 → 0.2.0.rc1
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -1
- data/CHANGES.md +5 -2
- data/Gemfile +6 -4
- data/disposable.gemspec +2 -1
- data/lib/disposable/callback.rb +19 -26
- data/lib/disposable/expose.rb +3 -3
- data/lib/disposable/{twin/schema.rb → rescheme.rb} +11 -11
- data/lib/disposable/twin/changed.rb +1 -1
- data/lib/disposable/twin/composition.rb +3 -3
- data/lib/disposable/twin/definitions.rb +28 -0
- data/lib/disposable/twin/setup.rb +6 -5
- data/lib/disposable/twin/struct.rb +2 -2
- data/lib/disposable/twin/sync.rb +20 -11
- data/lib/disposable/twin.rb +20 -55
- data/lib/disposable/version.rb +1 -1
- data/lib/disposable.rb +1 -0
- data/test/callback_group_test.rb +14 -13
- data/test/expose_test.rb +2 -2
- data/test/{twin/schema_test.rb → rescheme_test.rb} +34 -36
- data/test/test_helper.rb +3 -2
- data/test/twin/collection_test.rb +5 -5
- data/test/twin/feature_test.rb +0 -4
- data/test/twin/inherit_test.rb +2 -2
- data/test/twin/process_inline_test.rb +26 -26
- data/test/twin/setup_test.rb +3 -3
- data/test/twin/twin_test.rb +10 -25
- metadata +26 -13
- data/lib/disposable/twin/option.rb +0 -12
- data/lib/disposable/twin/representer.rb +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a33b386392c77411ef19323562c9f0bb3b31761c
|
|
4
|
+
data.tar.gz: 789982e39d11feb8a4550e93452ca8d9ba91ac07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb94a6c5097a5b380011f9a6cf6f6b02081b058842751110beec5b2f82cc37fc088f08d8b3ea2b8fb3d87e8607c9e44acc446d52470394f4ca27736cf8c29891
|
|
7
|
+
data.tar.gz: d11e58f841ecccfb165f6c48e6ddeb65eb3e9fe8b4aade955b6fc906bd056abc63589c9d42f05c1a35effb45becd6d0a6a616a171c81797d28fce1ac871895cf
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
# 0.
|
|
1
|
+
# 0.2.0
|
|
2
2
|
|
|
3
|
-
*
|
|
3
|
+
* Internally, use [Declarative](https://github.com/apotonick/declarative) now for schema creation, resulting in the following internal changes.
|
|
4
|
+
* `Twin::representer_class.representable_attrs` is now `Twin::definitions`.
|
|
5
|
+
* `Disposable::Twin::Schema` is now `Disposable::Rescheme`. Renamed its `:representer_from` option to `:definitions_from`.
|
|
6
|
+
* `twin: Twin::Song` is the only way to specify an external twin. To reduce complexity, I've removed the lambda version of `:twin`.
|
|
4
7
|
|
|
5
8
|
# 0.1.14
|
|
6
9
|
|
data/Gemfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
source
|
|
2
|
-
|
|
3
|
-
# Specify your gem's dependencies in disposable.gemspec
|
|
1
|
+
source "https://rubygems.org"
|
|
4
2
|
gemspec
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
# gem "representable", path: "../representable"
|
|
5
|
+
gem "representable", github: "apotonick/representable"
|
|
6
|
+
# gem "declarative", path: "../declarative"
|
|
7
|
+
# gem "declarative", github: "apotonick/declarative"
|
|
8
|
+
gem "minitest-line"
|
data/disposable.gemspec
CHANGED
|
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
21
|
spec.add_dependency "uber"
|
|
22
|
-
spec.add_dependency "
|
|
22
|
+
spec.add_dependency "declarative", "~> 0.0.4"
|
|
23
|
+
spec.add_dependency "representable", ">= 2.4.0.rc1", "< 2.5.0"
|
|
23
24
|
|
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.3"
|
|
25
26
|
spec.add_development_dependency "rake"
|
data/lib/disposable/callback.rb
CHANGED
|
@@ -12,26 +12,16 @@ module Disposable::Callback
|
|
|
12
12
|
# you can call collection :songs again, with :inherit. TODO: verify.
|
|
13
13
|
|
|
14
14
|
class Group
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# class << self
|
|
20
|
-
# include Representable::Cloneable
|
|
21
|
-
# end
|
|
22
|
-
self.representer_class = Class.new(Representable::Decorator) do
|
|
23
|
-
def self.default_inline_class
|
|
24
|
-
Group.extend Representable::Cloneable
|
|
25
|
-
end
|
|
15
|
+
extend Declarative::Schema
|
|
16
|
+
|
|
17
|
+
def self.default_nested_class
|
|
18
|
+
Group
|
|
26
19
|
end
|
|
27
20
|
|
|
28
21
|
def self.clone
|
|
29
22
|
Class.new(self)
|
|
30
23
|
end
|
|
31
24
|
|
|
32
|
-
def self.feature(*args)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
25
|
def self.collection(name, options={}, &block)
|
|
36
26
|
property(name, options.merge(collection: true), &block)
|
|
37
27
|
end
|
|
@@ -42,9 +32,9 @@ module Disposable::Callback
|
|
|
42
32
|
# it should have a Definition per callback where the representer_module will be a nested Group or a Callback.
|
|
43
33
|
inherit = options[:inherit] # FIXME: this is deleted in ::property.
|
|
44
34
|
|
|
45
|
-
|
|
35
|
+
super(name, options, &block).tap do |dfn|
|
|
46
36
|
return if inherit
|
|
47
|
-
hooks << ["property", dfn
|
|
37
|
+
hooks << ["property", dfn[:name]]
|
|
48
38
|
end
|
|
49
39
|
end
|
|
50
40
|
|
|
@@ -60,12 +50,16 @@ module Disposable::Callback
|
|
|
60
50
|
|
|
61
51
|
attr_reader :invocations
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
def self.hooks
|
|
54
|
+
@hooks ||= []
|
|
55
|
+
end
|
|
56
|
+
|
|
65
57
|
|
|
66
58
|
class << self
|
|
67
59
|
%w(on_add on_delete on_destroy on_update on_create on_change).each do |event|
|
|
68
60
|
define_method event do |*args|
|
|
61
|
+
heritage.record(event, *args)
|
|
62
|
+
|
|
69
63
|
hooks << [event.to_sym, args]
|
|
70
64
|
end
|
|
71
65
|
end
|
|
@@ -73,14 +67,13 @@ module Disposable::Callback
|
|
|
73
67
|
|
|
74
68
|
|
|
75
69
|
def call(options={})
|
|
76
|
-
self.class.hooks.each do |
|
|
77
|
-
event, args = cfg
|
|
78
|
-
|
|
70
|
+
self.class.hooks.each do |event, args|
|
|
79
71
|
if event == "property" # FIXME: make nicer.
|
|
80
|
-
definition = self.class.
|
|
81
|
-
twin = @twin.send(definition
|
|
72
|
+
definition = self.class.definitions.get(args)
|
|
73
|
+
twin = @twin.send(definition[:name]) # album.songs
|
|
82
74
|
|
|
83
|
-
|
|
75
|
+
# recursively call nested group.
|
|
76
|
+
@invocations += definition[:nested].new(twin).(options).invocations # Group.new(twin).()
|
|
84
77
|
next
|
|
85
78
|
end
|
|
86
79
|
|
|
@@ -108,7 +101,7 @@ module Disposable::Callback
|
|
|
108
101
|
# Implements the binding between the Callback API (on_change) and the underlying layer (twin/AR/etc.).
|
|
109
102
|
class Dispatch
|
|
110
103
|
def initialize(twins)
|
|
111
|
-
@twins = twins
|
|
104
|
+
@twins = Array(twins) # TODO: find that out with Collection.
|
|
112
105
|
@invocations = []
|
|
113
106
|
end
|
|
114
107
|
|
|
@@ -175,4 +168,4 @@ module Disposable::Callback
|
|
|
175
168
|
end
|
|
176
169
|
end
|
|
177
170
|
end
|
|
178
|
-
end
|
|
171
|
+
end
|
data/lib/disposable/expose.rb
CHANGED
|
@@ -13,8 +13,8 @@ module Disposable
|
|
|
13
13
|
# AlbumExpose.new(OpenStruct.new(title: "AFI")).name #=> "AFI"
|
|
14
14
|
class Expose
|
|
15
15
|
class << self
|
|
16
|
-
def from(
|
|
17
|
-
|
|
16
|
+
def from(schema)
|
|
17
|
+
schema.each do |definition|
|
|
18
18
|
process_definition!(definition)
|
|
19
19
|
end
|
|
20
20
|
self
|
|
@@ -22,7 +22,7 @@ module Disposable
|
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
def process_definition!(definition)
|
|
25
|
-
public_name = definition
|
|
25
|
+
public_name = definition[:name]
|
|
26
26
|
private_name = definition[:private_name] || public_name
|
|
27
27
|
|
|
28
28
|
accessors!(public_name, private_name, definition)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Rescheme::from allows to copy a schema structure. This will create "fresh" inline schemas instead
|
|
2
2
|
# of inheriting/copying the original classes, making it a replication of the structure, only.
|
|
3
3
|
#
|
|
4
|
-
# Options allow to customize the copied
|
|
4
|
+
# Options allow to customize the copied schema.
|
|
5
5
|
#
|
|
6
6
|
# +:exclude+: ignore options from original Definition when copying.
|
|
7
7
|
#
|
|
8
8
|
# Provided block is run per newly created Definition.
|
|
9
|
-
#
|
|
10
|
-
class Disposable::
|
|
9
|
+
# Rescheme.from(...) { |dfn| dfn[:readable] = true }
|
|
10
|
+
class Disposable::Rescheme
|
|
11
11
|
def self.from(*args, &block)
|
|
12
12
|
new.from(*args, &block)
|
|
13
13
|
end
|
|
@@ -16,9 +16,9 @@ class Disposable::Twin::Schema
|
|
|
16
16
|
def from(source_class, options, &block) # TODO: can we re-use this for all the decorator logic in #validate, etc?
|
|
17
17
|
representer = build_representer(options)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
definitions = options[:definitions_from].call(source_class)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
definitions.each do |dfn|
|
|
22
22
|
dfn = build_definition!(options, dfn, representer, &block)
|
|
23
23
|
evaluate_block!(options, dfn, &block)
|
|
24
24
|
end
|
|
@@ -41,7 +41,7 @@ private
|
|
|
41
41
|
new_options.merge!(local_options)
|
|
42
42
|
|
|
43
43
|
return from_scalar!(options, source_dfn, new_options, representer) if options[:recursive]==false
|
|
44
|
-
return from_scalar!(options, source_dfn, new_options, representer) unless source_dfn[:
|
|
44
|
+
return from_scalar!(options, source_dfn, new_options, representer) unless source_dfn[:nested]
|
|
45
45
|
from_inline!(options, source_dfn, new_options, representer, &block)
|
|
46
46
|
end
|
|
47
47
|
|
|
@@ -52,14 +52,14 @@ private
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def from_scalar!(options, dfn, new_options, representer)
|
|
55
|
-
representer.property(dfn
|
|
55
|
+
representer.property(dfn[:name], new_options)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def from_inline!(options, dfn, new_options, representer, &block)
|
|
59
|
-
nested = dfn[:
|
|
60
|
-
dfn_options = new_options.merge(
|
|
59
|
+
nested = dfn[:nested]#.evaluate(nil) # nested now can be a Decorator, a representer module, a Form, a Twin.
|
|
60
|
+
dfn_options = new_options.merge(nested: from(nested, options, &block))
|
|
61
61
|
|
|
62
|
-
representer.property(dfn
|
|
62
|
+
representer.property(dfn[:name], dfn_options)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def evaluate_block!(options, definition)
|
|
@@ -6,7 +6,7 @@ module Disposable
|
|
|
6
6
|
module Expose
|
|
7
7
|
module ClassMethods
|
|
8
8
|
def expose_class
|
|
9
|
-
@expose_class ||= Class.new(Disposable::Expose).from(
|
|
9
|
+
@expose_class ||= Class.new(Disposable::Expose).from(definitions.values)
|
|
10
10
|
end
|
|
11
11
|
end # ClassMethods.
|
|
12
12
|
|
|
@@ -26,7 +26,7 @@ module Disposable
|
|
|
26
26
|
module Composition
|
|
27
27
|
module ClassMethods
|
|
28
28
|
def expose_class
|
|
29
|
-
@expose_class ||= Class.new(Disposable::Composition).from(
|
|
29
|
+
@expose_class ||= Class.new(Disposable::Composition).from(definitions.values)
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -39,7 +39,7 @@ module Disposable
|
|
|
39
39
|
hash = {}
|
|
40
40
|
|
|
41
41
|
@model.each do |name, model| # TODO: provide list of composee attributes in Composition.
|
|
42
|
-
part_properties =
|
|
42
|
+
part_properties = schema.find_all { |dfn| dfn[:on] == name }.collect{ |dfn| dfn[:name].to_sym }
|
|
43
43
|
hash[name] = self.class.nested_hash_representer.new(self).to_hash(include: part_properties)
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Disposable::Twin
|
|
2
|
+
class Definition < Declarative::Definitions::Definition
|
|
3
|
+
def getter
|
|
4
|
+
self[:name]
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def setter
|
|
8
|
+
"#{self[:name]}="
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module DefinitionsEach
|
|
13
|
+
def each(options={})
|
|
14
|
+
return self unless block_given?
|
|
15
|
+
|
|
16
|
+
super() do |dfn|
|
|
17
|
+
next if options[:exclude] and options[:exclude].include?(dfn[:name])
|
|
18
|
+
next if options[:scalar] and dfn[:collection]
|
|
19
|
+
next if options[:collection] and ! dfn[:collection]
|
|
20
|
+
next if options[:twin] and ! dfn[:nested]
|
|
21
|
+
|
|
22
|
+
yield dfn
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -24,8 +24,8 @@ module Disposable
|
|
|
24
24
|
|
|
25
25
|
def setup_property!(dfn, options)
|
|
26
26
|
value =
|
|
27
|
-
if options.has_key?(name = dfn
|
|
28
|
-
options[dfn
|
|
27
|
+
if options.has_key?(name = dfn[:name].to_sym)
|
|
28
|
+
options[dfn[:name].to_sym]
|
|
29
29
|
else
|
|
30
30
|
setup_value_for(dfn, options)
|
|
31
31
|
end
|
|
@@ -39,17 +39,18 @@ module Disposable
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def read_value_for(dfn, options)
|
|
42
|
-
mapper.send(dfn
|
|
42
|
+
mapper.send(dfn[:name]) # model.title.
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def setup_write!(dfn, value)
|
|
46
|
-
send(dfn.setter, value)
|
|
46
|
+
# send(dfn.setter, value)
|
|
47
|
+
send("#{dfn[:name]}=", value) # FIXME!
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
# Including this will _not_ use the property's setter in Setup and allow you to override it.
|
|
50
51
|
module SkipSetter
|
|
51
52
|
def setup_write!(dfn, value)
|
|
52
|
-
write_property(dfn
|
|
53
|
+
write_property(dfn[:name], value, dfn)
|
|
53
54
|
end
|
|
54
55
|
end
|
|
55
56
|
end # Setup
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# Twin.new(id: 1)
|
|
6
6
|
module Struct
|
|
7
7
|
def read_value_for(dfn, options)
|
|
8
|
-
name = dfn
|
|
8
|
+
name = dfn[:name]
|
|
9
9
|
@model[name.to_s] || @model[name.to_sym] # TODO: test sym vs. str.
|
|
10
10
|
end
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
prepare: lambda { |model, *| model },
|
|
16
16
|
serialize: lambda { |model, *| model.sync! },
|
|
17
17
|
representable: true
|
|
18
|
-
) if dfn[:
|
|
18
|
+
) if dfn[:nested]
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
data/lib/disposable/twin/sync.rb
CHANGED
|
@@ -5,12 +5,23 @@
|
|
|
5
5
|
# Note: #sync currently implicitly saves AR objects with collections
|
|
6
6
|
class Disposable::Twin
|
|
7
7
|
module Sync
|
|
8
|
+
class Options < ::Hash
|
|
9
|
+
def exclude!(names)
|
|
10
|
+
excludes.push(*names)
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def excludes
|
|
15
|
+
self[:exclude] ||= []
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
8
19
|
# Creates a fresh copy of the internal representer and adds Representable::Hash.
|
|
9
20
|
# This is used wherever a hash transformation is needed.
|
|
10
21
|
def self.hash_representer(twin_class, &block)
|
|
11
|
-
|
|
22
|
+
Disposable::Rescheme.from(twin_class,
|
|
12
23
|
recursive: false,
|
|
13
|
-
|
|
24
|
+
definitions_from: lambda { |twin_class| twin_class.definitions },
|
|
14
25
|
superclass: Representable::Decorator,
|
|
15
26
|
include: Representable::Hash,
|
|
16
27
|
exclude_options: [:default], # TODO: TEST IN default_test.
|
|
@@ -25,19 +36,16 @@ class Disposable::Twin
|
|
|
25
36
|
end
|
|
26
37
|
alias_method :sync, :sync_models
|
|
27
38
|
|
|
28
|
-
# reading from fields allows using readers in form for presentation
|
|
29
|
-
# and writers still pass to fields in #validate????
|
|
30
|
-
|
|
31
39
|
# Sync all scalar attributes, call sync! on nested and return model.
|
|
32
40
|
def sync!(options) # semi-public.
|
|
33
41
|
# TODO: merge this into Sync::Run or something and use in Struct, too, so we don't
|
|
34
42
|
# need the representer anymore?
|
|
35
|
-
options_for_sync = sync_options(
|
|
43
|
+
options_for_sync = sync_options(Options[options])
|
|
36
44
|
|
|
37
45
|
schema.each(options_for_sync) do |dfn|
|
|
38
46
|
property_value = sync_read(dfn) #
|
|
39
47
|
|
|
40
|
-
unless dfn[:
|
|
48
|
+
unless dfn[:nested]
|
|
41
49
|
mapper.send(dfn.setter, property_value) # always sync the property
|
|
42
50
|
next
|
|
43
51
|
end
|
|
@@ -61,6 +69,7 @@ class Disposable::Twin
|
|
|
61
69
|
send(definition.getter)
|
|
62
70
|
end
|
|
63
71
|
|
|
72
|
+
# TODO: simplify that using a decent pipeline from Representable.
|
|
64
73
|
module ToNestedHash
|
|
65
74
|
def to_nested_hash(*)
|
|
66
75
|
self.class.nested_hash_representer.new(nested_hash_source).to_hash
|
|
@@ -86,7 +95,7 @@ class Disposable::Twin
|
|
|
86
95
|
dfn.merge!(
|
|
87
96
|
prepare: lambda { |model, *| model }, # TODO: why do we need that here?
|
|
88
97
|
serialize: lambda { |form, args| form.to_nested_hash },
|
|
89
|
-
) if dfn[:
|
|
98
|
+
) if dfn[:nested]
|
|
90
99
|
end
|
|
91
100
|
end # #build_nested_hash_representer
|
|
92
101
|
end
|
|
@@ -107,7 +116,7 @@ class Disposable::Twin
|
|
|
107
116
|
def sync_options(options)
|
|
108
117
|
options = super
|
|
109
118
|
|
|
110
|
-
protected_fields = schema.each.find_all { |d| d[:writeable] == false }.collect { |d| d
|
|
119
|
+
protected_fields = schema.each.find_all { |d| d[:writeable] == false }.collect { |d| d[:name] }
|
|
111
120
|
options.exclude!(protected_fields)
|
|
112
121
|
end
|
|
113
122
|
end
|
|
@@ -126,7 +135,7 @@ class Disposable::Twin
|
|
|
126
135
|
def sync_options(options)
|
|
127
136
|
# DISCUSS: we currently don't track if nested forms have changed (only their attributes). that's why i include them all here, which
|
|
128
137
|
# is additional sync work/slightly wrong. solution: allow forms to form.changed? not sure how to do that with collections.
|
|
129
|
-
scalars = schema.each(scalar: true).collect { |dfn| dfn
|
|
138
|
+
scalars = schema.each(scalar: true).collect { |dfn| dfn[:name ]}
|
|
130
139
|
unchanged = scalars - changed.keys
|
|
131
140
|
|
|
132
141
|
# exclude unchanged scalars, nested forms and changed scalars still go in here!
|
|
@@ -139,7 +148,7 @@ class Disposable::Twin
|
|
|
139
148
|
# Include this won't use the getter #title in #sync but read directly from @fields.
|
|
140
149
|
module SkipGetter
|
|
141
150
|
def sync_read(dfn)
|
|
142
|
-
@fields[dfn
|
|
151
|
+
@fields[dfn[:name]]
|
|
143
152
|
end
|
|
144
153
|
|
|
145
154
|
def nested_hash_source
|
data/lib/disposable/twin.rb
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
# DISCUSS: sync via @fields, not reader? allows overriding a la reform 1.
|
|
2
|
-
|
|
3
1
|
require "uber/inheritable_attr"
|
|
4
|
-
|
|
5
|
-
require "disposable/twin/
|
|
2
|
+
require "declarative/schema"
|
|
3
|
+
require "disposable/twin/definitions"
|
|
6
4
|
require "disposable/twin/collection"
|
|
7
5
|
require "disposable/twin/setup"
|
|
8
6
|
require "disposable/twin/sync"
|
|
9
7
|
require "disposable/twin/save"
|
|
10
|
-
require "disposable/twin/option"
|
|
11
8
|
require "disposable/twin/builder"
|
|
12
9
|
require "disposable/twin/changed"
|
|
13
10
|
require "disposable/twin/property_processor"
|
|
14
11
|
require "disposable/twin/persisted"
|
|
15
12
|
require "disposable/twin/default"
|
|
16
13
|
|
|
14
|
+
require "representable/decorator"
|
|
15
|
+
|
|
17
16
|
# Twin.new(model/composition hash/hash, options)
|
|
18
17
|
# assign hash to @fields
|
|
19
18
|
# write: write to @fields
|
|
@@ -21,29 +20,20 @@ require "disposable/twin/default"
|
|
|
21
20
|
|
|
22
21
|
module Disposable
|
|
23
22
|
class Twin
|
|
24
|
-
extend
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self.representer_class = Class.new(Decorator)
|
|
28
|
-
|
|
29
|
-
# Returns an each'able array of all properties defined in this twin.
|
|
30
|
-
# Allows to filter using
|
|
31
|
-
# * collection: true
|
|
32
|
-
# * twin: true
|
|
33
|
-
# * scalar: true
|
|
34
|
-
# * exclude: ["title", "email"]
|
|
35
|
-
def schema
|
|
36
|
-
self.class.representer_class
|
|
23
|
+
extend Declarative::Schema
|
|
24
|
+
def self.definition_class
|
|
25
|
+
Definition
|
|
37
26
|
end
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def self.register_feature(mod)
|
|
42
|
-
representer_class.representable_attrs[:features][mod] = true
|
|
28
|
+
def schema
|
|
29
|
+
self.class.definitions.extend(DefinitionsEach)
|
|
43
30
|
end
|
|
44
31
|
|
|
45
|
-
|
|
46
32
|
class << self
|
|
33
|
+
def default_nested_class
|
|
34
|
+
Twin
|
|
35
|
+
end
|
|
36
|
+
|
|
47
37
|
# TODO: move to Declarative, as in Representable and Reform.
|
|
48
38
|
def property(name, options={}, &block)
|
|
49
39
|
options[:private_name] = options.delete(:from) || name
|
|
@@ -52,18 +42,10 @@ module Disposable
|
|
|
52
42
|
options[:writeable] = options[:readable] = false
|
|
53
43
|
end
|
|
54
44
|
|
|
55
|
-
options[:
|
|
45
|
+
options[:nested] = options.delete(:twin) if options[:twin]
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
super(name, options, &block).tap do |definition|
|
|
58
48
|
create_accessors(name, definition)
|
|
59
|
-
|
|
60
|
-
if definition[:extend] and !options[:twin]
|
|
61
|
-
# This will soon be replaced with Declarative's API. # DISCUSS: could we use build_inline's api here to inject the name feature?
|
|
62
|
-
nested_twin = definition[:extend].evaluate(nil)
|
|
63
|
-
process_inline!(nested_twin, definition)
|
|
64
|
-
|
|
65
|
-
definition.merge!(twin: nested_twin) # DISCUSS: where do we need this?
|
|
66
|
-
end
|
|
67
49
|
end
|
|
68
50
|
end
|
|
69
51
|
|
|
@@ -80,14 +62,10 @@ module Disposable
|
|
|
80
62
|
mod = Module.new do
|
|
81
63
|
define_method(name) { @fields[name.to_s] }
|
|
82
64
|
# define_method(name) { read_property(name) }
|
|
83
|
-
define_method("#{name}=") { |value| write_property(name, value, definition) }
|
|
65
|
+
define_method("#{name}=") { |value| write_property(name, value, definition) }
|
|
84
66
|
end
|
|
85
67
|
include mod
|
|
86
68
|
end
|
|
87
|
-
|
|
88
|
-
# DISCUSS: this method might disappear or change pretty soon.
|
|
89
|
-
def process_inline!(mod, definition)
|
|
90
|
-
end
|
|
91
69
|
end
|
|
92
70
|
|
|
93
71
|
include Setup
|
|
@@ -97,8 +75,8 @@ module Disposable
|
|
|
97
75
|
private
|
|
98
76
|
# assumption: collections are always initialized from Setup since we assume an empty [] for "nil"/uninitialized collections.
|
|
99
77
|
def write_property(name, value, dfn)
|
|
100
|
-
if dfn[:
|
|
101
|
-
value = dfn
|
|
78
|
+
if dfn[:nested] and value
|
|
79
|
+
value = dfn[:collection] ? wrap_collection(dfn, value) : wrap_scalar(dfn, value)
|
|
102
80
|
end
|
|
103
81
|
|
|
104
82
|
field_write(name, value)
|
|
@@ -124,24 +102,13 @@ module Disposable
|
|
|
124
102
|
end
|
|
125
103
|
include Accessors
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
# FIXME: this is experimental.
|
|
129
|
-
module ToS
|
|
130
|
-
def to_s
|
|
131
|
-
return super if self.class.name
|
|
132
|
-
"#<Twin (inline):#{object_id}>"
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
include ToS
|
|
136
|
-
|
|
137
|
-
|
|
138
105
|
class Twinner
|
|
139
106
|
def initialize(dfn)
|
|
140
107
|
@dfn = dfn
|
|
141
108
|
end
|
|
142
109
|
|
|
143
110
|
def call(value)
|
|
144
|
-
@dfn.
|
|
111
|
+
@dfn[:nested].new(value)
|
|
145
112
|
end
|
|
146
113
|
end
|
|
147
114
|
|
|
@@ -152,7 +119,5 @@ module Disposable
|
|
|
152
119
|
attr_reader :mapper
|
|
153
120
|
end
|
|
154
121
|
include ModelReaders
|
|
155
|
-
|
|
156
|
-
include Option
|
|
157
122
|
end
|
|
158
|
-
end
|
|
123
|
+
end
|
data/lib/disposable/version.rb
CHANGED
data/lib/disposable.rb
CHANGED
data/test/callback_group_test.rb
CHANGED
|
@@ -148,10 +148,10 @@ class CallbackGroupInheritanceTest < MiniTest::Spec
|
|
|
148
148
|
it do
|
|
149
149
|
Group.hooks.size.must_equal 4
|
|
150
150
|
Group.hooks[0].to_s.must_equal "[:on_change, [:change!]]"
|
|
151
|
-
# Group.hooks[1][1].
|
|
151
|
+
# Group.hooks[1][1][:nested].hooks.to_s.must_equal "[[:on_add, [:notify_album!]],[:on_add, [:reset_song!]]]"
|
|
152
152
|
Group.hooks[2].to_s.must_equal "[:on_change, [:rehash_name!, {:property=>:title}]]"
|
|
153
153
|
|
|
154
|
-
Group.
|
|
154
|
+
Group.definitions.get(Group.hooks[3][1])[:nested].hooks.to_s.must_equal "[[:on_change, [:sing!]]]"
|
|
155
155
|
end
|
|
156
156
|
|
|
157
157
|
class EmptyGroup < Group
|
|
@@ -173,8 +173,9 @@ class CallbackGroupInheritanceTest < MiniTest::Spec
|
|
|
173
173
|
|
|
174
174
|
it do
|
|
175
175
|
Group.hooks.size.must_equal 4
|
|
176
|
+
pp EnhancedGroup.hooks
|
|
176
177
|
EnhancedGroup.hooks.size.must_equal 6
|
|
177
|
-
EnhancedGroup.
|
|
178
|
+
EnhancedGroup.definitions.get(EnhancedGroup.hooks[5][1])[:nested].hooks.to_s.must_equal "[[:on_add, [:rewind!]]]"
|
|
178
179
|
end
|
|
179
180
|
|
|
180
181
|
class EnhancedWithInheritGroup < EnhancedGroup
|
|
@@ -190,10 +191,10 @@ class CallbackGroupInheritanceTest < MiniTest::Spec
|
|
|
190
191
|
Group.hooks.size.must_equal 4
|
|
191
192
|
EnhancedGroup.hooks.size.must_equal 6
|
|
192
193
|
|
|
193
|
-
EnhancedGroup.
|
|
194
|
+
EnhancedGroup.definitions.get(EnhancedGroup.hooks[5][1])[:nested].hooks.to_s.must_equal "[[:on_add, [:rewind!]]]"
|
|
194
195
|
EnhancedWithInheritGroup.hooks.size.must_equal 6
|
|
195
|
-
EnhancedWithInheritGroup.
|
|
196
|
-
EnhancedWithInheritGroup.
|
|
196
|
+
EnhancedWithInheritGroup.definitions.get(EnhancedWithInheritGroup.hooks[1][1])[:nested].hooks.to_s.must_equal "[[:on_add, [:rewind!]], [:on_add, [:eat!]]]"
|
|
197
|
+
EnhancedWithInheritGroup.definitions.get(EnhancedWithInheritGroup.hooks[3][1])[:nested].hooks.to_s.must_equal "[[:on_change, [:sing!]], [:on_delete, [:yell!]]]"
|
|
197
198
|
end
|
|
198
199
|
|
|
199
200
|
class RemovingInheritGroup < Group
|
|
@@ -205,19 +206,19 @@ class CallbackGroupInheritanceTest < MiniTest::Spec
|
|
|
205
206
|
|
|
206
207
|
# # puts "@@@@@ #{Group.hooks.object_id.inspect}"
|
|
207
208
|
# # puts "@@@@@ #{EmptyGroup.hooks.object_id.inspect}"
|
|
208
|
-
# puts "@@@@@ Group: #{Group.
|
|
209
|
-
# puts "@@@@@ EnhancedGroup: #{EnhancedGroup.
|
|
210
|
-
# puts "@@@@@ InheritGroup: #{EnhancedWithInheritGroup.
|
|
211
|
-
# puts "@@@@@ RemovingGroup: #{RemovingInheritGroup.
|
|
212
|
-
# # puts "@@@@@ #{EnhancedWithInheritGroup.
|
|
209
|
+
# puts "@@@@@ Group: #{Group.definitions.get(:songs)[:nested].hooks.inspect}"
|
|
210
|
+
# puts "@@@@@ EnhancedGroup: #{EnhancedGroup.definitions.get(:songs)[:nested].hooks.inspect}"
|
|
211
|
+
# puts "@@@@@ InheritGroup: #{EnhancedWithInheritGroup.definitions.get(:songs)[:nested].hooks.inspect}"
|
|
212
|
+
# puts "@@@@@ RemovingGroup: #{RemovingInheritGroup.definitions.get(:songs)[:nested].hooks.inspect}"
|
|
213
|
+
# # puts "@@@@@ #{EnhancedWithInheritGroup.definitions.get(:songs)[:nested].hooks.object_id.inspect}"
|
|
213
214
|
|
|
214
215
|
# TODO: object_id tests for all nested representers.
|
|
215
216
|
|
|
216
217
|
it do
|
|
217
218
|
Group.hooks.size.must_equal 4
|
|
218
219
|
RemovingInheritGroup.hooks.size.must_equal 3
|
|
219
|
-
RemovingInheritGroup.
|
|
220
|
-
RemovingInheritGroup.
|
|
220
|
+
RemovingInheritGroup.definitions.get(RemovingInheritGroup.hooks[0][1])[:nested].hooks.to_s.must_equal "[[:on_add, [:reset_song!]]]"
|
|
221
|
+
RemovingInheritGroup.definitions.get(RemovingInheritGroup.hooks[2][1])[:nested].hooks.to_s.must_equal "[[:on_change, [:sing!]]]"
|
|
221
222
|
end
|
|
222
223
|
|
|
223
224
|
# Group::clone
|
data/test/expose_test.rb
CHANGED
|
@@ -16,7 +16,7 @@ class ExposeTest < MiniTest::Spec
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
class AlbumExpose < Disposable::Expose
|
|
19
|
-
from Twin::Album.
|
|
19
|
+
from Twin::Album.definitions.values
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
let (:album) { Model::Album.new(1, "Dick Sandwich") }
|
|
@@ -59,7 +59,7 @@ class ExposeCompositionTest < MiniTest::Spec
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
class AlbumComposition < Disposable::Composition
|
|
62
|
-
from Twin::Album.
|
|
62
|
+
from Twin::Album.definitions.values
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class SchemaTest < MiniTest::Spec
|
|
3
|
+
class ReschemeTest < MiniTest::Spec
|
|
6
4
|
module Representer
|
|
7
5
|
include Representable
|
|
8
6
|
|
|
@@ -32,80 +30,80 @@ class SchemaTest < MiniTest::Spec
|
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
it do
|
|
35
|
-
decorator = Disposable::
|
|
36
|
-
include: [Hello, Gday, Ciao], # Hello will win over Gday.
|
|
33
|
+
decorator = Disposable::Rescheme.from(Representer, superclass: Representable::Decorator,
|
|
34
|
+
include: [Representable::Hash, Hello, Gday, Ciao], # Hello will win over Gday.
|
|
37
35
|
options_from: :deserializer,
|
|
38
|
-
|
|
36
|
+
definitions_from: lambda { |nested| nested.definitions }
|
|
39
37
|
)
|
|
40
38
|
|
|
41
39
|
# include: works.
|
|
42
40
|
decorator.new(nil).hello.must_equal "hello"
|
|
43
41
|
decorator.new(nil).ciao.must_equal "ciao"
|
|
44
42
|
|
|
45
|
-
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:parse_filter=>[], :render_filter=>[]
|
|
46
|
-
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :deserializer=>{:skip_parse=>\"skip lambda\"}, :parse_filter=>[], :render_filter=>[], :
|
|
43
|
+
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:name=>\"id\", :parse_filter=>[], :render_filter=>[]}>"
|
|
44
|
+
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :deserializer=>{:skip_parse=>\"skip lambda\"}, :name=>\"title\", :parse_filter=>[], :render_filter=>[], :skip_parse=>\"skip lambda\"}>"
|
|
47
45
|
|
|
48
46
|
songs = decorator.representable_attrs.get(:songs)
|
|
49
47
|
options = songs.instance_variable_get(:@options)
|
|
50
|
-
|
|
51
|
-
options.inspect.must_equal "{:readable=>false, :deserializer=>{:skip_parse=>\"another lambda\", :music=>true, :writeable=>false}, :
|
|
48
|
+
options[:nested].extend(Declarative::Inspect)
|
|
49
|
+
options.inspect.must_equal "{:readable=>false, :deserializer=>{:skip_parse=>\"another lambda\", :music=>true, :writeable=>false}, :nested=>#<Class:>, :extend=>#<Class:>, :name=>\"songs\", :parse_filter=>[], :render_filter=>[], :skip_parse=>\"another lambda\", :music=>true, :writeable=>false}"
|
|
52
50
|
|
|
53
51
|
# nested works.
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
options[:nested].new(nil).hello.must_equal "hello"
|
|
53
|
+
options[:nested].new(nil).ciao.must_equal "ciao"
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
options[:nested].representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :deserializer=>{:skip_parse=>\"a crazy cool instance method\"}, :name=>\"name\", :parse_filter=>[], :render_filter=>[], :skip_parse=>\"a crazy cool instance method\"}>"
|
|
58
56
|
end
|
|
59
57
|
|
|
60
58
|
# :options_from and :include is optional
|
|
61
59
|
it do
|
|
62
|
-
decorator = Disposable::
|
|
63
|
-
|
|
60
|
+
decorator = Disposable::Rescheme.from(Representer, superclass: Representable::Decorator, include: [Representable::Hash],
|
|
61
|
+
definitions_from: lambda { |nested| nested.definitions }
|
|
64
62
|
)
|
|
65
63
|
|
|
66
|
-
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:parse_filter=>[], :render_filter=>[]
|
|
67
|
-
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :deserializer=>{:skip_parse=>\"skip lambda\"}, :parse_filter=>[], :render_filter=>[]
|
|
64
|
+
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:name=>\"id\", :parse_filter=>[], :render_filter=>[]}>"
|
|
65
|
+
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :deserializer=>{:skip_parse=>\"skip lambda\"}, :name=>\"title\", :parse_filter=>[], :render_filter=>[]}>"
|
|
68
66
|
end
|
|
69
67
|
|
|
70
68
|
|
|
71
69
|
# :exclude_options allows skipping particular options when copying.
|
|
72
70
|
it do
|
|
73
|
-
decorator = Disposable::
|
|
74
|
-
|
|
71
|
+
decorator = Disposable::Rescheme.from(Representer, superclass: Representable::Decorator, include: [Representable::Hash],
|
|
72
|
+
definitions_from: lambda { |nested| nested.definitions },
|
|
75
73
|
exclude_options: [:deserializer]
|
|
76
74
|
)
|
|
77
75
|
|
|
78
|
-
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:parse_filter=>[], :render_filter=>[]
|
|
79
|
-
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :parse_filter=>[], :render_filter=>[]
|
|
80
|
-
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :parse_filter=>[], :render_filter=>[]}>"
|
|
76
|
+
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:name=>\"id\", :parse_filter=>[], :render_filter=>[]}>"
|
|
77
|
+
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :name=>\"title\", :parse_filter=>[], :render_filter=>[]}>"
|
|
78
|
+
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :name=>\"name\", :parse_filter=>[], :render_filter=>[]}>"
|
|
81
79
|
end
|
|
82
80
|
|
|
83
81
|
|
|
84
82
|
it "::from with block allows customizing every definition and returns representer" do
|
|
85
|
-
decorator = Disposable::
|
|
83
|
+
decorator = Disposable::Rescheme.from(Representer, include: [Representable::Hash],
|
|
86
84
|
superclass: Representable::Decorator,
|
|
87
|
-
|
|
85
|
+
definitions_from: lambda { |nested| nested.definitions },
|
|
88
86
|
) { |dfn| dfn.merge!(amazing: true) }
|
|
89
87
|
|
|
90
|
-
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:parse_filter=>[], :render_filter=>[], :
|
|
91
|
-
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :deserializer=>{:skip_parse=>\"a crazy cool instance method\"}, :parse_filter=>[], :render_filter=>[], :amazing=>true}>"
|
|
88
|
+
decorator.representable_attrs.get(:id).inspect.must_equal "#<Representable::Definition ==>id @options={:name=>\"id\", :parse_filter=>[], :render_filter=>[], :amazing=>true}>"
|
|
89
|
+
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :deserializer=>{:skip_parse=>\"a crazy cool instance method\"}, :name=>\"name\", :parse_filter=>[], :render_filter=>[], :amazing=>true}>"
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
it "recursive: false only copies first level" do
|
|
95
|
-
decorator = Disposable::
|
|
93
|
+
decorator = Disposable::Rescheme.from(Representer, include: [Representable::Hash],
|
|
96
94
|
superclass: Representable::Decorator,
|
|
97
|
-
|
|
95
|
+
definitions_from: lambda { |nested| nested.definitions },
|
|
98
96
|
recursive: false,
|
|
99
97
|
exclude_options: [:deserializer]
|
|
100
98
|
)
|
|
101
99
|
|
|
102
|
-
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :parse_filter=>[], :render_filter=>[]
|
|
103
|
-
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :deserializer=>{:skip_parse=>\"a crazy cool instance method\"}, :parse_filter=>[], :render_filter=>[]}>"
|
|
100
|
+
decorator.representable_attrs.get(:title).inspect.must_equal "#<Representable::Definition ==>title @options={:writeable=>false, :name=>\"title\", :parse_filter=>[], :render_filter=>[]}>"
|
|
101
|
+
decorator.representable_attrs.get(:songs).representer_module.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:as=>\"Name\", :deserializer=>{:skip_parse=>\"a crazy cool instance method\"}, :name=>\"name\", :parse_filter=>[], :render_filter=>[]}>"
|
|
104
102
|
end
|
|
105
103
|
end
|
|
106
104
|
|
|
107
105
|
|
|
108
|
-
class
|
|
106
|
+
class TwinReschemeTest < MiniTest::Spec
|
|
109
107
|
class Artist < Disposable::Twin
|
|
110
108
|
property :name
|
|
111
109
|
end
|
|
@@ -115,15 +113,15 @@ class TwinSchemaTest < MiniTest::Spec
|
|
|
115
113
|
end
|
|
116
114
|
|
|
117
115
|
it do
|
|
118
|
-
decorator = Disposable::
|
|
119
|
-
|
|
116
|
+
decorator = Disposable::Rescheme.from(Album, superclass: Representable::Decorator, include: [Representable::Hash],
|
|
117
|
+
definitions_from: lambda { |nested| nested.definitions }
|
|
120
118
|
)
|
|
121
119
|
|
|
122
120
|
artist = decorator.representable_attrs.get(:artist)
|
|
123
121
|
options = artist.instance_variable_get(:@options)
|
|
124
|
-
nested_extend = options
|
|
125
|
-
options.inspect.must_equal "{:
|
|
122
|
+
nested_extend = options[:nested]
|
|
123
|
+
options.extend(Declarative::Inspect).inspect.must_equal "{:private_name=>:artist, :nested=>#<Class:>, :name=>\"artist\", :extend=>#<Class:>, :parse_filter=>[], :render_filter=>[]}"
|
|
126
124
|
assert nested_extend < Representable::Decorator
|
|
127
|
-
nested_extend.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:private_name=>:name, :parse_filter=>[], :render_filter=>[]
|
|
125
|
+
nested_extend.representable_attrs.get(:name).inspect.must_equal "#<Representable::Definition ==>name @options={:private_name=>:name, :name=>\"name\", :parse_filter=>[], :render_filter=>[]}>"
|
|
128
126
|
end
|
|
129
127
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -16,13 +16,13 @@ class TwinCollectionTest < MiniTest::Spec
|
|
|
16
16
|
class Song < Disposable::Twin
|
|
17
17
|
property :id # DISCUSS: needed for #save.
|
|
18
18
|
property :title
|
|
19
|
-
property :album, :
|
|
19
|
+
# property :album, twin: Album
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
class Album < Disposable::Twin
|
|
23
23
|
property :id # DISCUSS: needed for #save.
|
|
24
24
|
property :name
|
|
25
|
-
collection :songs, :
|
|
25
|
+
collection :songs, twin: Song
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -62,8 +62,8 @@ class TwinCollectionActiveRecordTest < MiniTest::Spec
|
|
|
62
62
|
class Album < Disposable::Twin
|
|
63
63
|
property :id # DISCUSS: needed for #save.
|
|
64
64
|
property :name
|
|
65
|
-
collection :songs, :
|
|
66
|
-
property :artist, twin:
|
|
65
|
+
collection :songs, twin: Twin::Song
|
|
66
|
+
property :artist, twin: Twin::Artist
|
|
67
67
|
|
|
68
68
|
include Sync
|
|
69
69
|
include Save
|
|
@@ -208,7 +208,7 @@ class CollectionUnitTest < MiniTest::Spec
|
|
|
208
208
|
Album = Struct.new(:id, :name, :songs, :artist)
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
let(:collection) { Disposable::Twin::Collection.new(Disposable::Twin::Twinner.new(Twin::Song.
|
|
211
|
+
let(:collection) { Disposable::Twin::Collection.new(Disposable::Twin::Twinner.new(Twin::Song.definitions.get(:album)), []) }
|
|
212
212
|
|
|
213
213
|
# #insert(index, model)
|
|
214
214
|
it do
|
data/test/twin/feature_test.rb
CHANGED
|
@@ -18,23 +18,19 @@ class FeatureTest < MiniTest::Spec
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
class AlbumForm < Disposable::Twin
|
|
21
|
-
include Setup
|
|
22
21
|
feature Date
|
|
23
22
|
property :name
|
|
24
23
|
|
|
25
24
|
collection :songs do
|
|
26
|
-
include Setup
|
|
27
25
|
property :title
|
|
28
26
|
|
|
29
27
|
property :composer do
|
|
30
|
-
include Setup
|
|
31
28
|
feature Instrument
|
|
32
29
|
property :name
|
|
33
30
|
end
|
|
34
31
|
end
|
|
35
32
|
|
|
36
33
|
property :artist do
|
|
37
|
-
include Setup
|
|
38
34
|
property :name
|
|
39
35
|
end
|
|
40
36
|
end
|
data/test/twin/inherit_test.rb
CHANGED
|
@@ -40,8 +40,8 @@ class InheritTest < MiniTest::Spec
|
|
|
40
40
|
|
|
41
41
|
# definitions are not shared.
|
|
42
42
|
it do
|
|
43
|
-
Twin::Album.
|
|
44
|
-
Twin::Compilation.
|
|
43
|
+
Twin::Album.definitions.get(:name).extend(Declarative::Inspect).inspect.must_equal "#<Disposable::Twin::Definition: @options={:fromage=>:_name, :private_name=>:name, :name=>\"name\"}>"
|
|
44
|
+
Twin::Compilation.definitions.get(:name).extend(Declarative::Inspect).inspect.must_equal "#<Disposable::Twin::Definition: @options={:fromage=>:_name, :private_name=>:name, :name=>\"name\", :writeable=>false}>" # FIXME: where did :inherit go?
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
require "test_helper"
|
|
1
|
+
# require "test_helper"
|
|
2
2
|
|
|
3
|
-
class ProcessInlineTest < MiniTest::Spec
|
|
4
|
-
|
|
3
|
+
# class ProcessInlineTest < MiniTest::Spec
|
|
4
|
+
# Album = Struct.new(:artist, :composer, :recursive_composer)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# module InlineTwin
|
|
7
|
+
# end
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
# class RecursiveComposerTwin < Disposable::Twin
|
|
10
|
+
# property :composer, twin: self
|
|
11
|
+
# end
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
# class AlbumTwin < Disposable::Twin
|
|
14
|
+
# def self.process_inline!(inline_class, definition)
|
|
15
|
+
# inline_class.send :include, InlineTwin
|
|
16
|
+
# end
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# property :artist do
|
|
19
|
+
# end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
# property :composer, twin: ->{ ComposerTwin }
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
# property :recursive_composer, twin: RecursiveComposerTwin
|
|
24
|
+
# end
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# class ComposerTwin < Disposable::Twin
|
|
27
|
+
# end
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
end
|
|
29
|
+
# it do
|
|
30
|
+
# twin = AlbumTwin.new(Album.new(Object, Object))
|
|
31
|
+
# assert ! (twin.class < InlineTwin)
|
|
32
|
+
# assert (twin.artist.class < InlineTwin)
|
|
33
|
+
# assert ! (twin.composer.class < InlineTwin)
|
|
34
|
+
# end
|
|
35
|
+
# end
|
data/test/twin/setup_test.rb
CHANGED
|
@@ -17,7 +17,7 @@ class TwinSetupTest < MiniTest::Spec
|
|
|
17
17
|
|
|
18
18
|
class Song < Disposable::Twin
|
|
19
19
|
property :id
|
|
20
|
-
property :composer, twin:
|
|
20
|
+
property :composer, twin: Twin::Artist
|
|
21
21
|
|
|
22
22
|
include Setup
|
|
23
23
|
end
|
|
@@ -25,8 +25,8 @@ class TwinSetupTest < MiniTest::Spec
|
|
|
25
25
|
class Album < Disposable::Twin
|
|
26
26
|
property :id
|
|
27
27
|
property :name
|
|
28
|
-
collection :songs, twin:
|
|
29
|
-
property :artist, twin:
|
|
28
|
+
collection :songs, twin: Twin::Song
|
|
29
|
+
property :artist, twin: Twin::Artist
|
|
30
30
|
|
|
31
31
|
include Setup
|
|
32
32
|
end
|
data/test/twin/twin_test.rb
CHANGED
|
@@ -7,25 +7,24 @@ class TwinTest < MiniTest::Spec
|
|
|
7
7
|
Artist = Struct.new(:id)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# test twin: option
|
|
11
11
|
module Twin
|
|
12
|
+
class Artist < Disposable::Twin
|
|
13
|
+
property :id
|
|
14
|
+
|
|
15
|
+
include Setup
|
|
16
|
+
end
|
|
17
|
+
|
|
12
18
|
class Album < Disposable::Twin
|
|
13
19
|
property :id # DISCUSS: needed for #save.
|
|
14
20
|
property :name
|
|
15
|
-
|
|
16
|
-
property :artist, :twin => lambda { |*| Artist }
|
|
21
|
+
property :artist, twin: Artist
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
class Song < Disposable::Twin
|
|
20
25
|
property :id # DISCUSS: needed for #save.
|
|
21
26
|
property :title
|
|
22
|
-
property :album, :
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
class Artist < Disposable::Twin
|
|
26
|
-
property :id
|
|
27
|
-
|
|
28
|
-
include Setup
|
|
27
|
+
property :album, twin: Album
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
|
|
@@ -91,20 +90,6 @@ class TwinTest < MiniTest::Spec
|
|
|
91
90
|
# result.must_equal twin.album
|
|
92
91
|
# end
|
|
93
92
|
end
|
|
94
|
-
|
|
95
|
-
# FIXME: experimental.
|
|
96
|
-
describe "#to_s" do
|
|
97
|
-
class HitTwin < Disposable::Twin
|
|
98
|
-
include Setup
|
|
99
|
-
|
|
100
|
-
property :song do
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
let (:hit) { OpenStruct.new(song: song) }
|
|
105
|
-
it { HitTwin.new(hit).to_s.must_match "#<TwinTest::HitTwin:" }
|
|
106
|
-
it { HitTwin.new(hit).song.to_s.must_match "#<Twin (inline):" }
|
|
107
|
-
end
|
|
108
93
|
end
|
|
109
94
|
|
|
110
95
|
|
|
@@ -138,7 +123,7 @@ class TwinAsTest < MiniTest::Spec
|
|
|
138
123
|
|
|
139
124
|
class Song < Disposable::Twin
|
|
140
125
|
property :name, :from => :title
|
|
141
|
-
property :record, :
|
|
126
|
+
property :record, twin: Album, :from => :album
|
|
142
127
|
|
|
143
128
|
# model Model::Song
|
|
144
129
|
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.
|
|
4
|
+
version: 0.2.0.rc1
|
|
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-11-
|
|
11
|
+
date: 2015-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: uber
|
|
@@ -24,26 +24,40 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: declarative
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.0.4
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.0.4
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: representable
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - ">="
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 2.
|
|
47
|
+
version: 2.4.0.rc1
|
|
34
48
|
- - "<"
|
|
35
49
|
- !ruby/object:Gem::Version
|
|
36
|
-
version: 2.
|
|
50
|
+
version: 2.5.0
|
|
37
51
|
type: :runtime
|
|
38
52
|
prerelease: false
|
|
39
53
|
version_requirements: !ruby/object:Gem::Requirement
|
|
40
54
|
requirements:
|
|
41
55
|
- - ">="
|
|
42
56
|
- !ruby/object:Gem::Version
|
|
43
|
-
version: 2.
|
|
57
|
+
version: 2.4.0.rc1
|
|
44
58
|
- - "<"
|
|
45
59
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 2.
|
|
60
|
+
version: 2.5.0
|
|
47
61
|
- !ruby/object:Gem::Dependency
|
|
48
62
|
name: bundler
|
|
49
63
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -152,6 +166,7 @@ files:
|
|
|
152
166
|
- lib/disposable/callback.rb
|
|
153
167
|
- lib/disposable/composition.rb
|
|
154
168
|
- lib/disposable/expose.rb
|
|
169
|
+
- lib/disposable/rescheme.rb
|
|
155
170
|
- lib/disposable/twin.rb
|
|
156
171
|
- lib/disposable/twin/builder.rb
|
|
157
172
|
- lib/disposable/twin/changed.rb
|
|
@@ -159,12 +174,10 @@ files:
|
|
|
159
174
|
- lib/disposable/twin/collection.rb
|
|
160
175
|
- lib/disposable/twin/composition.rb
|
|
161
176
|
- lib/disposable/twin/default.rb
|
|
162
|
-
- lib/disposable/twin/
|
|
177
|
+
- lib/disposable/twin/definitions.rb
|
|
163
178
|
- lib/disposable/twin/persisted.rb
|
|
164
179
|
- lib/disposable/twin/property_processor.rb
|
|
165
|
-
- lib/disposable/twin/representer.rb
|
|
166
180
|
- lib/disposable/twin/save.rb
|
|
167
|
-
- lib/disposable/twin/schema.rb
|
|
168
181
|
- lib/disposable/twin/setup.rb
|
|
169
182
|
- lib/disposable/twin/struct.rb
|
|
170
183
|
- lib/disposable/twin/sync.rb
|
|
@@ -175,6 +188,7 @@ files:
|
|
|
175
188
|
- test/example.rb
|
|
176
189
|
- test/expose_test.rb
|
|
177
190
|
- test/persisted_test.rb
|
|
191
|
+
- test/rescheme_test.rb
|
|
178
192
|
- test/skip_getter_test.rb
|
|
179
193
|
- test/test_helper.rb
|
|
180
194
|
- test/twin/benchmarking.rb
|
|
@@ -193,7 +207,6 @@ files:
|
|
|
193
207
|
- test/twin/process_inline_test.rb
|
|
194
208
|
- test/twin/readable_test.rb
|
|
195
209
|
- test/twin/save_test.rb
|
|
196
|
-
- test/twin/schema_test.rb
|
|
197
210
|
- test/twin/setup_test.rb
|
|
198
211
|
- test/twin/skip_unchanged_test.rb
|
|
199
212
|
- test/twin/struct_test.rb
|
|
@@ -217,9 +230,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
217
230
|
version: '0'
|
|
218
231
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
232
|
requirements:
|
|
220
|
-
- - "
|
|
233
|
+
- - ">"
|
|
221
234
|
- !ruby/object:Gem::Version
|
|
222
|
-
version:
|
|
235
|
+
version: 1.3.1
|
|
223
236
|
requirements: []
|
|
224
237
|
rubyforge_project:
|
|
225
238
|
rubygems_version: 2.4.8
|
|
@@ -234,6 +247,7 @@ test_files:
|
|
|
234
247
|
- test/example.rb
|
|
235
248
|
- test/expose_test.rb
|
|
236
249
|
- test/persisted_test.rb
|
|
250
|
+
- test/rescheme_test.rb
|
|
237
251
|
- test/skip_getter_test.rb
|
|
238
252
|
- test/test_helper.rb
|
|
239
253
|
- test/twin/benchmarking.rb
|
|
@@ -252,7 +266,6 @@ test_files:
|
|
|
252
266
|
- test/twin/process_inline_test.rb
|
|
253
267
|
- test/twin/readable_test.rb
|
|
254
268
|
- test/twin/save_test.rb
|
|
255
|
-
- test/twin/schema_test.rb
|
|
256
269
|
- test/twin/setup_test.rb
|
|
257
270
|
- test/twin/skip_unchanged_test.rb
|
|
258
271
|
- test/twin/struct_test.rb
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module Disposable::Twin::Option
|
|
2
|
-
def self.included(base)
|
|
3
|
-
base.extend ClassMethods
|
|
4
|
-
end
|
|
5
|
-
|
|
6
|
-
module ClassMethods
|
|
7
|
-
def option(name, options={})
|
|
8
|
-
# default: nil will always set an option in the, even when not in the incoming options.
|
|
9
|
-
property(name, options.merge(readable: false, writeable: false, default: nil))
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
require "representable/decorator"
|
|
2
|
-
# require "representable/hash"
|
|
3
|
-
|
|
4
|
-
module Disposable
|
|
5
|
-
class Twin
|
|
6
|
-
class Decorator < Representable::Decorator
|
|
7
|
-
# Overrides representable's Definition class so we can add semantics in our representers.
|
|
8
|
-
class Definition < Representable::Definition
|
|
9
|
-
def dynamic_options
|
|
10
|
-
super + [:twin]
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def twin_class
|
|
14
|
-
self[:twin].evaluate(nil) # FIXME: do we support the :twin option, and should it be wrapped?
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# FIXME: this is not properly used when inheriting - fix that in representable.
|
|
19
|
-
def self.build_config
|
|
20
|
-
Config.new(Definition)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.each(options={})
|
|
24
|
-
return representable_attrs[:definitions].values unless block_given?
|
|
25
|
-
|
|
26
|
-
definitions = representable_attrs
|
|
27
|
-
|
|
28
|
-
definitions.each do |dfn|
|
|
29
|
-
next if options[:exclude] and options[:exclude].include?(dfn.name)
|
|
30
|
-
next if options[:scalar] and dfn[:collection]
|
|
31
|
-
next if options[:collection] and ! dfn[:collection]
|
|
32
|
-
next if options[:twin] and ! dfn[:twin]
|
|
33
|
-
|
|
34
|
-
yield dfn
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
definitions
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def self.default_inline_class
|
|
41
|
-
Twin
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class Options < ::Hash
|
|
46
|
-
def exclude!(names)
|
|
47
|
-
excludes.push(*names)
|
|
48
|
-
self
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def excludes
|
|
52
|
-
self[:exclude] ||= []
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end # Decorator.
|
|
56
|
-
end
|
|
57
|
-
end
|