disposable 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.md +9 -0
- data/STUFF +1 -8
- data/database.sqlite3 +0 -0
- data/disposable.gemspec +1 -1
- data/gemfiles/Gemfile.rails-2.3.lock +13 -2
- data/gemfiles/Gemfile.rails-3.0.lock +13 -2
- data/lib/disposable/composition.rb +30 -9
- data/lib/disposable/facade/active_record.rb +0 -3
- data/lib/disposable/twin.rb +84 -49
- data/lib/disposable/version.rb +1 -1
- data/test/composition_test.rb +96 -0
- data/test/twin/active_record_test.rb +61 -1
- data/test/twin/composition_test.rb +27 -40
- data/test/twin/twin_test.rb +57 -2
- metadata +26 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3028d579eaa9e41a6d3591c069809c98940bd7a1
|
4
|
+
data.tar.gz: 12371d78cdf1c2bfe046021f45ffb0a1dbd4ccff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2af96f209c219622ac48b55d86798cd392fd83cb73e14463319d965c20a01824e4354ff5f09e9b71bddc21caeb64e369cb265478c77572226c1245297a7f25bc
|
7
|
+
data.tar.gz: e58b5545c50a104ca16ae784cb48a2280dc60a407f35345dcccf78e867756380993826be1e6d4ce5f442475f0fd1311fddc4f2079f467cf5d1b774ce05b44c07
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Disposable
|
2
2
|
|
3
3
|
|
4
|
+
why?
|
5
|
+
because i want this to fuckin work: Wallpaper.find(51).update_attributes(enabled:false)
|
6
|
+
|
4
7
|
|
5
8
|
## Twin
|
6
9
|
|
@@ -82,6 +85,12 @@ song.save # write to DB.
|
|
82
85
|
|
83
86
|
|
84
87
|
|
88
|
+
## Composition
|
89
|
+
|
90
|
+
hiding composed datastructures
|
91
|
+
mapping methods, with optional as:
|
92
|
+
|
93
|
+
|
85
94
|
## To be documented properly
|
86
95
|
|
87
96
|
Facade existing (model) class
|
data/STUFF
CHANGED
@@ -1,11 +1,4 @@
|
|
1
1
|
USE CASE:
|
2
2
|
|
3
3
|
<% client.invoices.outstanding.each do |invoice| %>
|
4
|
-
<%= link_to invoice.invoice_number, {:controller => "party", :action => "invoice_show", :id => invoice.id, :entity_instance_id => client.party, :entity_type_id => EntityType::PARTY}, :onclick => "event.cancelBubble=true;" %> <%= number_to_currency(invoice.balance_out)
|
5
|
-
|
6
|
-
|
7
|
-
Invoice::Option::Rate <-- uses option everywhere
|
8
|
-
make that declaratively hook-able:
|
9
|
-
belongs_to Invoice::Option
|
10
|
-
|
11
|
-
Invoice::Option#rate => Rate.new(self)
|
4
|
+
<%= link_to invoice.invoice_number, {:controller => "party", :action => "invoice_show", :id => invoice.id, :entity_instance_id => client.party, :entity_type_id => EntityType::PARTY}, :onclick => "event.cancelBubble=true;" %> <%= number_to_currency(invoice.balance_out)
|
data/database.sqlite3
CHANGED
Binary file
|
data/disposable.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "uber"
|
22
|
-
spec.add_dependency "representable"
|
22
|
+
spec.add_dependency "representable", "~> 1.8.1"
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.3"
|
25
25
|
spec.add_development_dependency "rake"
|
@@ -1,7 +1,9 @@
|
|
1
1
|
PATH
|
2
|
-
remote:
|
2
|
+
remote: ../
|
3
3
|
specs:
|
4
|
-
disposable (0.0.
|
4
|
+
disposable (0.0.2)
|
5
|
+
representable (~> 1.8.1)
|
6
|
+
uber
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: http://rubygems.org/
|
@@ -16,7 +18,11 @@ GEM
|
|
16
18
|
activeresource (2.3.18)
|
17
19
|
activesupport (= 2.3.18)
|
18
20
|
activesupport (2.3.18)
|
21
|
+
mini_portile (0.5.3)
|
19
22
|
minitest (5.0.8)
|
23
|
+
multi_json (1.9.3)
|
24
|
+
nokogiri (1.6.1)
|
25
|
+
mini_portile (~> 0.5.0)
|
20
26
|
rack (1.1.6)
|
21
27
|
rails (2.3.18)
|
22
28
|
actionmailer (= 2.3.18)
|
@@ -26,7 +32,12 @@ GEM
|
|
26
32
|
activesupport (= 2.3.18)
|
27
33
|
rake (>= 0.8.3)
|
28
34
|
rake (10.1.0)
|
35
|
+
representable (1.8.1)
|
36
|
+
multi_json
|
37
|
+
nokogiri
|
38
|
+
uber
|
29
39
|
sqlite3 (1.3.8)
|
40
|
+
uber (0.0.4)
|
30
41
|
|
31
42
|
PLATFORMS
|
32
43
|
ruby
|
@@ -1,7 +1,9 @@
|
|
1
1
|
PATH
|
2
|
-
remote:
|
2
|
+
remote: ../
|
3
3
|
specs:
|
4
|
-
disposable (0.0.
|
4
|
+
disposable (0.0.2)
|
5
|
+
representable (~> 1.8.1)
|
6
|
+
uber
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: http://rubygems.org/
|
@@ -33,7 +35,11 @@ GEM
|
|
33
35
|
abstract (>= 1.0.0)
|
34
36
|
i18n (0.5.0)
|
35
37
|
json (1.8.0)
|
38
|
+
mini_portile (0.5.3)
|
36
39
|
minitest (5.0.8)
|
40
|
+
multi_json (1.9.3)
|
41
|
+
nokogiri (1.6.1)
|
42
|
+
mini_portile (~> 0.5.0)
|
37
43
|
rack (1.2.8)
|
38
44
|
rack-mount (0.6.14)
|
39
45
|
rack (>= 1.0.0)
|
@@ -48,9 +54,14 @@ GEM
|
|
48
54
|
rake (10.1.0)
|
49
55
|
rdoc (3.12.2)
|
50
56
|
json (~> 1.4)
|
57
|
+
representable (1.8.1)
|
58
|
+
multi_json
|
59
|
+
nokogiri
|
60
|
+
uber
|
51
61
|
sqlite3 (1.3.8)
|
52
62
|
thor (0.14.6)
|
53
63
|
tzinfo (0.3.38)
|
64
|
+
uber (0.0.4)
|
54
65
|
|
55
66
|
PLATFORMS
|
56
67
|
ruby
|
@@ -1,40 +1,55 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Disposable
|
2
4
|
# Composition delegates accessors to models as per configuration.
|
5
|
+
# Composition doesn't know anything but methods (readers and writers) to expose and the mappings to
|
6
|
+
# the internal models. Optionally, it knows about renamings such as mapping `#song_id` to `song.id`.
|
3
7
|
#
|
4
8
|
# class Album
|
5
9
|
# include Disposable::Composition
|
6
|
-
|
7
|
-
# map( {cd: [:id, :name], band: [:title]} )
|
10
|
+
#
|
11
|
+
# map( {cd: [[:id], [:name]], band: [[:id, :band_id], [:title]]} )
|
8
12
|
# end
|
9
|
-
|
13
|
+
#
|
10
14
|
# album = Album.new(cd: CD.find(1), band: Band.new)
|
11
15
|
# album.id #=> 1
|
12
16
|
# album.title = "Ten Foot Pole"
|
13
17
|
module Composition
|
14
18
|
def self.included(base)
|
19
|
+
base.extend(Forwardable)
|
15
20
|
base.extend(ClassMethods)
|
16
21
|
end
|
17
22
|
|
23
|
+
|
18
24
|
module ClassMethods
|
19
25
|
def map(options)
|
20
|
-
|
26
|
+
@map = {}
|
21
27
|
|
22
28
|
options.each do |mdl, meths|
|
23
|
-
create_accessors(mdl, meths)
|
24
29
|
attr_reader mdl
|
25
30
|
|
26
|
-
meths.each
|
31
|
+
meths.each do |mtd| # [[:title], [:id, :song_id]]
|
32
|
+
create_accessors(mdl, mtd)
|
33
|
+
add_to_map(mdl, mtd)
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
29
37
|
|
38
|
+
private
|
30
39
|
def create_accessors(model, methods)
|
31
|
-
|
32
|
-
|
40
|
+
def_instance_delegator model, *methods # reader
|
41
|
+
def_instance_delegator model, *methods.map { |m| "#{m}=" } # writer
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_to_map(model, methods)
|
45
|
+
name, public_name = methods
|
46
|
+
public_name ||= name
|
47
|
+
|
48
|
+
@map[public_name.to_sym] = {:method => name.to_sym, :model => model.to_sym}
|
33
49
|
end
|
34
50
|
end
|
35
51
|
|
36
52
|
|
37
|
-
private
|
38
53
|
def initialize(models)
|
39
54
|
models.each do |name, obj|
|
40
55
|
instance_variable_set(:"@#{name}", obj)
|
@@ -43,6 +58,12 @@ module Disposable
|
|
43
58
|
@_models = models.values
|
44
59
|
end
|
45
60
|
|
61
|
+
# Allows multiplexing method calls to all composed models.
|
62
|
+
def each(&block)
|
63
|
+
_models.each(&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
46
67
|
attr_reader:_models
|
47
68
|
end
|
48
69
|
end
|
@@ -2,9 +2,6 @@ class Disposable::Facade
|
|
2
2
|
module ActiveRecord
|
3
3
|
def is_a?(klass)
|
4
4
|
# DISCUSS: should we use facade_options here for the class?
|
5
|
-
#return self.class.facade_options.first == klass if self.class.facade_options.first
|
6
|
-
# DISCUSS: make ::facades obligatory?
|
7
|
-
|
8
5
|
klass == __getobj__.class or super
|
9
6
|
end
|
10
7
|
end
|
data/lib/disposable/twin.rb
CHANGED
@@ -13,36 +13,14 @@ module Disposable
|
|
13
13
|
Class.new(self) # By subclassing, representable_attrs.clone is called.
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.twin_names
|
21
|
-
representable_attrs.twin_names
|
22
|
-
end
|
23
|
-
|
24
|
-
def twins(&block)
|
25
|
-
clone_config!.
|
26
|
-
find_all { |attr| attr[:form] }.
|
27
|
-
each(&block)
|
28
|
-
end
|
29
|
-
|
30
|
-
module ConfigExtensions
|
31
|
-
def twin_names
|
16
|
+
def twin_names
|
17
|
+
representable_attrs.
|
32
18
|
find_all { |attr| attr[:twin] }.
|
33
|
-
collect { |attr| attr.name }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
class Save < self
|
39
|
-
|
19
|
+
collect { |attr| attr.name.to_sym }
|
40
20
|
end
|
41
21
|
end
|
42
22
|
|
43
23
|
|
44
|
-
|
45
|
-
|
46
24
|
extend Uber::InheritableAttr
|
47
25
|
inheritable_attr :representer_class
|
48
26
|
self.representer_class = Class.new(Decorator)
|
@@ -53,10 +31,13 @@ module Disposable
|
|
53
31
|
self._model = name
|
54
32
|
end
|
55
33
|
|
56
|
-
def self.property(name,
|
57
|
-
|
34
|
+
def self.property(name, options={}, &block)
|
35
|
+
options[:public_name] = options.delete(:as) || name
|
36
|
+
options[:pass_options] = true
|
58
37
|
|
59
|
-
representer_class.property(name,
|
38
|
+
representer_class.property(name, options, &block).tap do |definition|
|
39
|
+
attr_accessor definition[:public_name]
|
40
|
+
end
|
60
41
|
end
|
61
42
|
|
62
43
|
def self.from(model) # TODO: private.
|
@@ -72,49 +53,68 @@ module Disposable
|
|
72
53
|
new(_model.find(id))
|
73
54
|
end
|
74
55
|
|
56
|
+
# hash for #update_attributes (model API): {title: "Future World", album: <Album>}
|
75
57
|
def self.save_representer
|
76
58
|
# TODO: do that only at compile-time!
|
77
|
-
save = Class.new(
|
59
|
+
save = Class.new(write_representer) # inherit configuration
|
78
60
|
save.representable_attrs.
|
79
61
|
find_all { |attr| attr[:twin] }.
|
80
62
|
each { |attr| attr.merge!(
|
81
|
-
:representable => true
|
63
|
+
:representable => true,
|
64
|
+
:serialize => lambda { |obj, args| obj.send(:model) }) }
|
65
|
+
|
66
|
+
save.representable_attrs.each do |attr|
|
67
|
+
attr.merge!(:as => attr.name)
|
68
|
+
end
|
69
|
+
|
82
70
|
save
|
83
71
|
end
|
84
72
|
|
73
|
+
# transform incoming model into twin API hash.
|
85
74
|
def self.new_representer
|
86
75
|
representer = Class.new(representer_class) # inherit configuration
|
76
|
+
|
77
|
+
# wrap incoming nested model in it's Twin.
|
87
78
|
representer.representable_attrs.
|
88
79
|
find_all { |attr| attr[:twin] }.
|
89
80
|
each { |attr| attr.merge!(
|
90
|
-
:pass_options => true,
|
91
81
|
:prepare => lambda { |object, args| args.binding[:twin].new(object) }) }
|
92
|
-
representer
|
93
|
-
end
|
94
82
|
|
83
|
+
# song_title => model.title
|
84
|
+
representer.representable_attrs.each do |attr|
|
85
|
+
attr.merge!(:as => attr[:public_name])
|
86
|
+
end
|
95
87
|
|
96
|
-
|
97
|
-
model
|
88
|
+
representer
|
98
89
|
end
|
99
90
|
|
100
|
-
#
|
101
|
-
def
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
91
|
+
# read/write to twin using twin's API (e.g. #record= not #album=).
|
92
|
+
def self.write_representer
|
93
|
+
representer = Class.new(representer_class) # inherit configuration
|
94
|
+
representer.representable_attrs.
|
95
|
+
each { |attr| attr.merge!(
|
96
|
+
# use the alias name (as:) when writing attributes in new.
|
97
|
+
# DISCUSS: attr.name = public_name would be simpler.
|
98
|
+
:as => attr[:public_name],
|
99
|
+
:getter => lambda { |args| send("#{args.binding[:public_name]}") },
|
100
|
+
:setter => lambda { |value, args| send("#{args.binding[:public_name]}=", value) }
|
101
|
+
)}
|
107
102
|
|
103
|
+
representer
|
104
|
+
end
|
108
105
|
|
109
|
-
|
110
|
-
|
111
|
-
|
106
|
+
# call save on all nested twins.
|
107
|
+
def self.pre_save_representer
|
108
|
+
representer = Class.new(write_representer)
|
109
|
+
representer.representable_attrs.
|
110
|
+
each { |attr| attr.merge!(
|
111
|
+
:representable => true,
|
112
|
+
:serialize => lambda { |model, args| model.save }
|
113
|
+
)}
|
112
114
|
|
113
|
-
|
114
|
-
self.id = model.id
|
115
|
+
representer
|
115
116
|
end
|
116
117
|
|
117
|
-
# below is the code for a representable-style twin:
|
118
118
|
|
119
119
|
# TODO: improve speed when setting up a twin.
|
120
120
|
def initialize(model, options={})
|
@@ -125,11 +125,46 @@ module Disposable
|
|
125
125
|
merge(options))
|
126
126
|
end
|
127
127
|
|
128
|
+
# it's important to stress that #save is the only entry point where we hit the database after initialize.
|
129
|
+
def save # use that in Reform::AR.
|
130
|
+
pre_save = self.class.pre_save_representer.new(self)
|
131
|
+
pre_save.to_hash(:include => pre_save.twin_names) # #save on nested Twins.
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
# what we do right now
|
136
|
+
# call save on all nested twins - how does that work with dependencies (eg Album needs Song id)?
|
137
|
+
# extract all ORM attributes
|
138
|
+
# write to model
|
139
|
+
|
140
|
+
sync_attrs = self.class.save_representer.new(self).to_hash
|
141
|
+
# puts "sync> #{sync_attrs.inspect}"
|
142
|
+
# this is ORM-specific:
|
143
|
+
model.update_attributes(sync_attrs) # this also does `album: #<Album>`
|
144
|
+
|
145
|
+
# FIXME: sync again, here, or just id?
|
146
|
+
self.id = model.id
|
147
|
+
end
|
148
|
+
|
128
149
|
private
|
129
150
|
def from_hash(options={})
|
130
|
-
self.class.
|
151
|
+
self.class.write_representer.new(self).from_hash(options)
|
131
152
|
end
|
132
153
|
|
133
154
|
attr_reader :model # TODO: test
|
155
|
+
|
156
|
+
|
157
|
+
# class Composition < self
|
158
|
+
# def initialize(hash)
|
159
|
+
# hash = hash.first
|
160
|
+
# composition = Class.new do
|
161
|
+
# include Disposable::Composition
|
162
|
+
# map( {:song => [:song_title], :requester => [:name]})
|
163
|
+
# self
|
164
|
+
# end.new(hash)
|
165
|
+
|
166
|
+
# super(composition)
|
167
|
+
# end
|
168
|
+
# end
|
134
169
|
end
|
135
170
|
end
|
data/lib/disposable/version.rb
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CompositionTest < MiniTest::Spec
|
4
|
+
module Model
|
5
|
+
Band = Struct.new(:id, :title,)
|
6
|
+
Album = Struct.new(:id, :name)
|
7
|
+
end
|
8
|
+
|
9
|
+
module Twin
|
10
|
+
class Album #< Disposable::Twin
|
11
|
+
include Disposable::Composition
|
12
|
+
|
13
|
+
map( {:album => [[:id], [:name]],
|
14
|
+
:band => [[:id, :band_id], [:title]]
|
15
|
+
} )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let (:band) { Model::Band.new(1, "Frenzal Rhomb") }
|
20
|
+
let (:album) { Model::Album.new(2, "Dick Sandwhich") }
|
21
|
+
subject { Twin::Album.new(:album => album, :band => band) }
|
22
|
+
|
23
|
+
describe "readers" do
|
24
|
+
it { subject.id.must_equal 2 }
|
25
|
+
it { subject.band_id.must_equal 1 }
|
26
|
+
it { subject.name.must_equal "Dick Sandwhich" }
|
27
|
+
it { subject.title.must_equal "Frenzal Rhomb" }
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "writers" do
|
32
|
+
before do
|
33
|
+
subject.id = 3
|
34
|
+
subject.band_id = 4
|
35
|
+
subject.name = "Eclipse"
|
36
|
+
subject.title = "Yngwie J. Malmsteen"
|
37
|
+
end
|
38
|
+
|
39
|
+
it { subject.id.must_equal 3 }
|
40
|
+
it { album.id.must_equal 3 }
|
41
|
+
it { subject.band_id.must_equal 4 }
|
42
|
+
it { band.id.must_equal 4 }
|
43
|
+
it { subject.name.must_equal "Eclipse" }
|
44
|
+
it { subject.title.must_equal "Yngwie J. Malmsteen" }
|
45
|
+
it { album.name.must_equal "Eclipse" }
|
46
|
+
it { band.title.must_equal "Yngwie J. Malmsteen" }
|
47
|
+
end
|
48
|
+
# it { subject.save }
|
49
|
+
|
50
|
+
it "raises when non-mapped property" do
|
51
|
+
assert_raises NoMethodError do
|
52
|
+
subject.raise_an_exception
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
describe "readers to models" do
|
58
|
+
it { subject.album.object_id.must_equal album.object_id }
|
59
|
+
it { subject.band.object_id.must_equal band.object_id }
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
describe "#each" do
|
64
|
+
it "what" do
|
65
|
+
results = []
|
66
|
+
subject.each { |mdl| results << mdl.object_id }
|
67
|
+
results.must_equal([album.object_id, band.object_id])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
describe "#_models" do
|
73
|
+
it { subject.send(:_models).must_equal([album, band]) }
|
74
|
+
it { Twin::Album.new(:album => album).send(:_models).must_equal([album]) }
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
describe "@map" do
|
79
|
+
let (:composition) {
|
80
|
+
Class.new do
|
81
|
+
include Disposable::Composition
|
82
|
+
|
83
|
+
map( {:album => [["id"], [:name]],
|
84
|
+
"band" => [[:id, "band_id"], [:title]]
|
85
|
+
} )
|
86
|
+
end
|
87
|
+
}
|
88
|
+
|
89
|
+
# yepp, a private test WITH interface violation, as this is still a semi-public concept.
|
90
|
+
it { composition.instance_variable_get(:@map).must_equal({
|
91
|
+
:id => {:method=>:id, :model=>:album},
|
92
|
+
:name => {:method=>:name, :model=>:album},
|
93
|
+
:band_id => {:method=>:id, :model=>:band},
|
94
|
+
:title => {:method=>:title, :model=>:band}}) }
|
95
|
+
end
|
96
|
+
end
|
@@ -85,6 +85,7 @@ class TwinActiveRecordTest < MiniTest::Spec
|
|
85
85
|
must_equal({"id" => subject.id, "title" => "Broken"}) }
|
86
86
|
|
87
87
|
it { subject.album.must_equal album }
|
88
|
+
it { subject.album.id.wont_equal nil } # FIXME: this only works because song is saved after album.
|
88
89
|
end
|
89
90
|
|
90
91
|
|
@@ -101,4 +102,63 @@ class TwinActiveRecordTest < MiniTest::Spec
|
|
101
102
|
|
102
103
|
it { subject.album.attributes.slice("name").must_equal("name" => "Billy Talent") }
|
103
104
|
end
|
104
|
-
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
class TwinActiveRecordAsTest < MiniTest::Spec
|
109
|
+
module Twin
|
110
|
+
class Album < Disposable::Twin
|
111
|
+
property :id
|
112
|
+
property :name, :as => :album_name
|
113
|
+
|
114
|
+
model ::Album
|
115
|
+
end
|
116
|
+
|
117
|
+
class Song < Disposable::Twin
|
118
|
+
property :id
|
119
|
+
property :title, :as => :song_title
|
120
|
+
property :album, :twin => Album, :as => :record
|
121
|
+
|
122
|
+
model ::Song
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
describe "::from" do
|
128
|
+
# (existing models)
|
129
|
+
let (:song) { ::Song.new(:title => "Broken", :album => album) }
|
130
|
+
let (:album) { ::Album.new(:name => "The Process Of Belief") }
|
131
|
+
|
132
|
+
let(:twin) { Twin::Song.from(song) }
|
133
|
+
|
134
|
+
it { twin.song_title.must_equal "Broken" }
|
135
|
+
it { twin.record.album_name.must_equal "The Process Of Belief" }
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
describe "#save" do
|
140
|
+
# existing models
|
141
|
+
let (:song) { ::Song.new(:title => "Broken", :album => album) }
|
142
|
+
let (:album) { ::Album.new(:name => "The Process Of Belief") }
|
143
|
+
|
144
|
+
let(:twin) { Twin::Song.from(song) }
|
145
|
+
|
146
|
+
before do
|
147
|
+
twin.song_title = "Emo Boy"
|
148
|
+
twin.record.album_name = "Rode Hard And Put Away Wet"
|
149
|
+
|
150
|
+
twin.save
|
151
|
+
end
|
152
|
+
|
153
|
+
let (:ar_song) { ::Song.find(twin.id) }
|
154
|
+
let (:ar_album) { ar_song.album }
|
155
|
+
|
156
|
+
it { ar_song.attributes.slice("id", "title").
|
157
|
+
must_equal({"id" => ar_song.id, "title" => "Emo Boy"}) }
|
158
|
+
|
159
|
+
it { ar_album.must_equal album }
|
160
|
+
it("xxx") { ar_album.name.must_equal "Rode Hard And Put Away Wet" }
|
161
|
+
it { ar_album.id.wont_equal nil } # FIXME: this only works because song is saved after album.
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
@@ -1,50 +1,37 @@
|
|
1
|
-
require 'test_helper'
|
1
|
+
# require 'test_helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
# class TwinCompositionTest < MiniTest::Spec
|
4
|
+
# class Request < Disposable::Twin::Composition
|
5
|
+
# property :title, :on => :song, :as => :song_title
|
6
|
+
# property :id, :on => :song, :as => :song_id
|
8
7
|
|
9
|
-
|
10
|
-
class Album #< Disposable::Twin
|
11
|
-
include Disposable::Composition
|
12
|
-
extend Disposable::Composition::ClassMethods # FIXME.
|
8
|
+
# property :name, :on => :requester
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
# property :title, :on => :band # as: :band_name
|
10
|
+
# # map ...
|
11
|
+
# end
|
17
12
|
|
18
|
-
|
13
|
+
# module Model
|
14
|
+
# Song = Struct.new(:id, :title, :album)
|
15
|
+
# Requester = Struct.new(:id, :name)
|
16
|
+
# end
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
18
|
+
# let (:requester) { Model::Requester.new(1, "Greg Howe") }
|
19
|
+
# let (:song) { Model::Song.new(2, "Extraction") }
|
23
20
|
|
24
|
-
|
25
|
-
let (:album) { Model::Album.new(2, "Dick Sandwhich") }
|
26
|
-
subject { Twin::Album.new(:album => album, :band => band) }
|
21
|
+
# let (:request) { Request.new([:song => song, :requester => requester]) }
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
it { subject.title.must_equal "Frenzal Rhomb" }
|
23
|
+
# it { request.song_title.must_equal "Extraction" }
|
24
|
+
# it { request.name.must_equal "Greg Howe" }
|
31
25
|
|
32
|
-
# it { subject.save }
|
33
26
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
# describe "setter" do
|
28
|
+
# before do
|
29
|
+
# request.song_title = "Tease"
|
30
|
+
# request.name = "Wooten"
|
31
|
+
# end
|
39
32
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
describe "#_models" do
|
47
|
-
it { subject.send(:_models).must_equal([album, band]) }
|
48
|
-
it { Twin::Album.new(:album => album).send(:_models).must_equal([album]) }
|
49
|
-
end
|
50
|
-
end
|
33
|
+
# it { request.song_title.must_equal "Tease" }
|
34
|
+
# it { song.title.must_equal "Tease" }
|
35
|
+
# it { request.name.must_equal "Wooten" }
|
36
|
+
# end
|
37
|
+
# end
|
data/test/twin/twin_test.rb
CHANGED
@@ -67,9 +67,9 @@ end
|
|
67
67
|
|
68
68
|
|
69
69
|
class TwinDecoratorTest < MiniTest::Spec
|
70
|
-
subject { TwinTest::Twin::Song.representer_class }
|
70
|
+
subject { TwinTest::Twin::Song.representer_class.new(nil) }
|
71
71
|
|
72
|
-
it { subject.twin_names.must_equal [
|
72
|
+
it { subject.twin_names.must_equal [:album] }
|
73
73
|
end
|
74
74
|
|
75
75
|
# from is as close to from_hash as possible
|
@@ -77,3 +77,58 @@ end
|
|
77
77
|
|
78
78
|
|
79
79
|
# should #new create empty associated models?
|
80
|
+
|
81
|
+
|
82
|
+
class TwinAsTest < MiniTest::Spec
|
83
|
+
module Model
|
84
|
+
Song = Struct.new(:title, :album)
|
85
|
+
Album = Struct.new(:name)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
module Twin
|
90
|
+
class Album < Disposable::Twin
|
91
|
+
property :name, :as => :record_name
|
92
|
+
|
93
|
+
model Model::Album
|
94
|
+
end
|
95
|
+
|
96
|
+
class Song < Disposable::Twin
|
97
|
+
property :title, :as => :name
|
98
|
+
property :album, :twin => Album, :as => :record
|
99
|
+
|
100
|
+
model Model::Song
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
let (:record) { Twin::Album.new(:record_name => "Veni Vidi Vicous") }
|
106
|
+
subject { Twin::Song.new(:name => "Outsmarted", :record => record) }
|
107
|
+
|
108
|
+
|
109
|
+
describe "::new" do # TODO: this creates a new model!
|
110
|
+
# the Twin exposes the as: API.
|
111
|
+
it { subject.name.must_equal "Outsmarted" }
|
112
|
+
it { subject.record.must_equal record }
|
113
|
+
end
|
114
|
+
|
115
|
+
# DISCUSS: should we test saving without AR? is that worth the hustle?
|
116
|
+
# describe "#save" do
|
117
|
+
# before { subject.send(:model).instance_eval do
|
118
|
+
# def update_attributes(*)
|
119
|
+
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
# subject.save
|
123
|
+
# }
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
# # before { subject.save }
|
128
|
+
|
129
|
+
# it { subject.name }
|
130
|
+
# end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# TODO: test coercion!
|
metadata
CHANGED
@@ -1,111 +1,111 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: disposable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
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-04-
|
11
|
+
date: 2014-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: uber
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: representable
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.8.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.8.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: minitest
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: activerecord
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: sqlite3
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
description: Domain-Oriented Refactoring Framework.
|
@@ -115,8 +115,8 @@ executables: []
|
|
115
115
|
extensions: []
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
|
-
- .gitignore
|
119
|
-
- .travis.yml
|
118
|
+
- ".gitignore"
|
119
|
+
- ".travis.yml"
|
120
120
|
- Gemfile
|
121
121
|
- LICENSE.txt
|
122
122
|
- README.md
|
@@ -135,6 +135,7 @@ files:
|
|
135
135
|
- lib/disposable/twin.rb
|
136
136
|
- lib/disposable/version.rb
|
137
137
|
- test/active_record_test.rb
|
138
|
+
- test/composition_test.rb
|
138
139
|
- test/facade_test.rb
|
139
140
|
- test/test_helper.rb
|
140
141
|
- test/twin/active_record_test.rb
|
@@ -150,24 +151,26 @@ require_paths:
|
|
150
151
|
- lib
|
151
152
|
required_ruby_version: !ruby/object:Gem::Requirement
|
152
153
|
requirements:
|
153
|
-
- -
|
154
|
+
- - ">="
|
154
155
|
- !ruby/object:Gem::Version
|
155
156
|
version: '0'
|
156
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
158
|
requirements:
|
158
|
-
- -
|
159
|
+
- - ">="
|
159
160
|
- !ruby/object:Gem::Version
|
160
161
|
version: '0'
|
161
162
|
requirements: []
|
162
163
|
rubyforge_project:
|
163
|
-
rubygems_version: 2.2.
|
164
|
+
rubygems_version: 2.2.1
|
164
165
|
signing_key:
|
165
166
|
specification_version: 4
|
166
167
|
summary: Domain-Oriented Refactoring Framework.
|
167
168
|
test_files:
|
168
169
|
- test/active_record_test.rb
|
170
|
+
- test/composition_test.rb
|
169
171
|
- test/facade_test.rb
|
170
172
|
- test/test_helper.rb
|
171
173
|
- test/twin/active_record_test.rb
|
172
174
|
- test/twin/composition_test.rb
|
173
175
|
- test/twin/twin_test.rb
|
176
|
+
has_rdoc:
|