disposable 0.0.2 → 0.0.3
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/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:
|