dnapi 1.1.74.jruby192.c
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dnapi.rb +54 -0
- data/lib/dnapi/app.rb +130 -0
- data/lib/dnapi/component.rb +50 -0
- data/lib/dnapi/component_possessor.rb +49 -0
- data/lib/dnapi/components/addons.rb +26 -0
- data/lib/dnapi/components/apache.rb +13 -0
- data/lib/dnapi/components/cloudkick.rb +13 -0
- data/lib/dnapi/components/encrypted_backup.rb +12 -0
- data/lib/dnapi/components/exim.rb +10 -0
- data/lib/dnapi/components/monitor.rb +12 -0
- data/lib/dnapi/components/nagios.rb +28 -0
- data/lib/dnapi/components/newrelic.rb +12 -0
- data/lib/dnapi/components/passenger3.rb +13 -0
- data/lib/dnapi/components/ruby.rb +234 -0
- data/lib/dnapi/components/ssmtp.rb +10 -0
- data/lib/dnapi/components/stunneled.rb +13 -0
- data/lib/dnapi/components/volume.rb +32 -0
- data/lib/dnapi/cron.rb +5 -0
- data/lib/dnapi/db_stack.rb +92 -0
- data/lib/dnapi/ebuild_dep.rb +5 -0
- data/lib/dnapi/environment.rb +327 -0
- data/lib/dnapi/extensions.rb +32 -0
- data/lib/dnapi/gem_dep.rb +9 -0
- data/lib/dnapi/instance.rb +69 -0
- data/lib/dnapi/monitoring.rb +22 -0
- data/lib/dnapi/recipe.rb +27 -0
- data/lib/dnapi/ssl_cert.rb +13 -0
- data/lib/dnapi/stack.rb +111 -0
- data/lib/dnapi/struct.rb +149 -0
- data/lib/dnapi/test.rb +114 -0
- data/lib/dnapi/test/ext.rb +32 -0
- data/lib/dnapi/test/sweatshop.rb +148 -0
- data/lib/dnapi/version.rb +3 -0
- data/lib/dnapi/vhost.rb +24 -0
- data/spec/app_spec.rb +68 -0
- data/spec/component_spec.rb +66 -0
- data/spec/components/addons_spec.rb +33 -0
- data/spec/components/cloudkick_spec.rb +17 -0
- data/spec/components/nagios_spec.rb +42 -0
- data/spec/components/nodejs_spec.rb +27 -0
- data/spec/components/passenger3_spec.rb +12 -0
- data/spec/components/ruby_spec.rb +321 -0
- data/spec/components/stunneled.rb +15 -0
- data/spec/components/volume_spec.rb +21 -0
- data/spec/db_stack_spec.rb +111 -0
- data/spec/environment_spec.rb +227 -0
- data/spec/instance_spec.rb +52 -0
- data/spec/proxies.rb +143 -0
- data/spec/proxies_spec.rb +76 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/stack_spec.rb +105 -0
- data/spec/struct_spec.rb +100 -0
- metadata +181 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::Environment do
|
4
|
+
|
5
|
+
def instance_info
|
6
|
+
@id ||= 0
|
7
|
+
@id += 1
|
8
|
+
{:id => @id,
|
9
|
+
:public_hostname => "public_host.#{@id}",
|
10
|
+
:private_hostname => "private_host.#{@id}"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def legacy_provision_instance
|
14
|
+
require 'ostruct'
|
15
|
+
OpenStruct.new(instance_info)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#db_host" do
|
19
|
+
it 'is the public hostname of the solo instance if the environment consists of a solo instance' do
|
20
|
+
environment = DNApi::Environment.new
|
21
|
+
environment.build_instance(instance_info.merge(:role => :solo))
|
22
|
+
environment.db_host.should eql('public_host.1')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'is the public hostname of the database server' do
|
26
|
+
environment = DNApi::Environment.new
|
27
|
+
db_instance_info = instance_info
|
28
|
+
environment.build_instance(db_instance_info.merge(:role => :db_master))
|
29
|
+
environment.db_host.should eql(db_instance_info[:public_hostname])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "responds to legacy 'api'" do
|
33
|
+
environment = DNApi::Environment.new
|
34
|
+
instance = legacy_provision_instance
|
35
|
+
environment.build_instance(:role => :db_master, :provisioned => instance)
|
36
|
+
environment.db_host.should eql(instance.public_hostname)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#db_slaves" do
|
41
|
+
it 'is an array of all instances with the db_slave role' do
|
42
|
+
environment = DNApi::Environment.new
|
43
|
+
environment.build_instance(instance_info.merge(:role => :app_master))
|
44
|
+
environment.build_instance(instance_info.merge(:role => :db_master))
|
45
|
+
environment.build_instance(instance_info.merge(:role => :db_slave))
|
46
|
+
environment.build_instance(instance_info.merge(:role => :db_slave))
|
47
|
+
environment.build_instance(instance_info.merge(:role => :db_slave))
|
48
|
+
environment.should have(3).db_slaves
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#utility_instances" do
|
53
|
+
it 'is an array of all instances with the utility_instances role' do
|
54
|
+
environment = DNApi::Environment.new
|
55
|
+
environment.build_instance(instance_info.merge(:role => :app_master))
|
56
|
+
environment.build_instance(instance_info.merge(:role => :db_master))
|
57
|
+
environment.build_instance(instance_info.merge(:role => :db_slave))
|
58
|
+
environment.build_instance(instance_info.merge(:role => :util, :name => "foo"))
|
59
|
+
environment.build_instance(instance_info.merge(:role => :util, :name => "bar"))
|
60
|
+
environment.should have(2).utility_instances
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#recipes' do
|
65
|
+
it 'includes the stack recipes' do
|
66
|
+
env = DNApi::Environment.new(:stack => DNApi::Stack::NginxMongrel)
|
67
|
+
env.build_app
|
68
|
+
env.recipes.should include(*DNApi::Stack::NginxMongrel.recipes)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'includes the app recipes' do
|
72
|
+
env = DNApi::Environment.new(:stack => DNApi::Stack::NginxMongrel)
|
73
|
+
env.build_app
|
74
|
+
env.recipes.should include(*DNApi::Recipe.defaults)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#mysql_backup_cron' do
|
79
|
+
it 'is nil if there is no backup_window' do
|
80
|
+
env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Mysql)
|
81
|
+
env.mysql_backup_cron.should be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'runs eybackup --quiet' do
|
85
|
+
env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Mysql)
|
86
|
+
env.mysql_backup_cron.command.should == 'eybackup --quiet'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'is 10 past the hour' do
|
90
|
+
env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Mysql)
|
91
|
+
env.mysql_backup_cron.minute.should == '10'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'is at 1 AM PST if the backup interal is 24 hours' do
|
95
|
+
env = DNApi::Environment.new(:backup_window => 1, :backup_interval => 24, :db_stack => DNApi::DbStack::Mysql)
|
96
|
+
env.mysql_backup_cron.hour.should == '1'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'is at every other hour if the backup interval is 12 hours' do
|
100
|
+
env = DNApi::Environment.new(:backup_window => 1, :backup_interval => 12, :db_stack => DNApi::DbStack::Mysql)
|
101
|
+
env.mysql_backup_cron.hour.should == '*/12'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#snapshot_cron' do
|
106
|
+
it 'runs ey-snapshots --snapshot --quiet' do
|
107
|
+
env = DNApi::Environment.new(:backup_window => 1)
|
108
|
+
env.snapshot_cron.command.should == 'ey-snapshots --snapshot --quiet'
|
109
|
+
end
|
110
|
+
it 'is in the last half of the hour' do
|
111
|
+
env = DNApi::Environment.new(:backup_window => 1)
|
112
|
+
env.snapshot_cron.minute.should =~ %r{0/[3-5]\d}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe '#postgres_backup_cron' do
|
117
|
+
it 'runs eybackup -e postgresql --quiet' do
|
118
|
+
env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Postgres)
|
119
|
+
env.postgres_backup_cron.command.should == 'eybackup -e postgresql --quiet'
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'is 20 past the hour' do
|
123
|
+
env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Postgres)
|
124
|
+
env.postgres_backup_cron.minute.should == '20'
|
125
|
+
end
|
126
|
+
|
127
|
+
context "disabled backups" do
|
128
|
+
it 'returns nil when backups are disabled on Postgres DbStack' do
|
129
|
+
env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres)
|
130
|
+
env.postgres_backup_cron.should be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'returns nil when backups are disabled on Postgres9 DbStack' do
|
134
|
+
env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres9)
|
135
|
+
env.postgres_backup_cron.should be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'returns nil when backups are disabled on Postgres91 DbStack' do
|
139
|
+
env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres91)
|
140
|
+
env.postgres_backup_cron.should be_nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "has newrelic" do
|
146
|
+
env = DNApi::Environment.new
|
147
|
+
|
148
|
+
env.newrelic_key = "foo"
|
149
|
+
env.newrelic_key.should == "foo"
|
150
|
+
|
151
|
+
env.build_app(:newrelic => true)
|
152
|
+
env.apps.first.newrelic?.should be_true
|
153
|
+
|
154
|
+
env.build_app.newrelic?.should be_false
|
155
|
+
end
|
156
|
+
|
157
|
+
it "ignores disabled instances when listing enabled app servers hostnames" do
|
158
|
+
env = DNApi.gen(:default, 'nginx_passenger', ['rails'])
|
159
|
+
env.build_instance(instance_info.merge(:role => :db_master, :enabled => false))
|
160
|
+
env.build_instance(instance_info.merge(:role => :app_master, :enabled => false))
|
161
|
+
instance = env.build_instance(instance_info.merge(:role => :app, :enabled => false))
|
162
|
+
env.enabled_app_servers_hostnames.should be_empty
|
163
|
+
|
164
|
+
dna = JSON.parse(instance.to_dna.to_json)
|
165
|
+
dna["members"].should be_empty
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '#backup_window' do
|
169
|
+
it 'should default to 14' do
|
170
|
+
DNApi::Environment.new.backup_window.should == 14
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "#dna_for_instance" do
|
175
|
+
it "raises an error when the instance is not found" do
|
176
|
+
env = DNApi.gen(:default, 'nginx_passenger', [])
|
177
|
+
instance = env.build_instance(instance_info.merge(:role => :solo))
|
178
|
+
lambda { DNApi::Environment.new.dna_for_instance(instance) }.
|
179
|
+
should raise_error(DNApi::InstanceNotFound)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
specify "monitoring" do
|
184
|
+
default = DNApi::Environment.new
|
185
|
+
default.monitoring.should be_monit
|
186
|
+
default.monitoring = "god"
|
187
|
+
default.monitoring.should be_god
|
188
|
+
|
189
|
+
monit = DNApi::Environment.new(:monitoring => "monit").monitoring
|
190
|
+
monit.should be_monit
|
191
|
+
monit.name.should == "monit"
|
192
|
+
monit.label.should == "Monit"
|
193
|
+
|
194
|
+
god = DNApi::Environment.new(:monitoring => "god").monitoring
|
195
|
+
god.should be_god
|
196
|
+
god.name.should == "god"
|
197
|
+
god.label.should == "god"
|
198
|
+
end
|
199
|
+
|
200
|
+
specify "components" do
|
201
|
+
env = DNApi::Environment.new
|
202
|
+
env.components.should be_empty
|
203
|
+
env.components << DNApi::Component[:god].new
|
204
|
+
env.has_component?(:god).should be_true
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "#components" do
|
208
|
+
it 'dumps components into a hash' do
|
209
|
+
env = DNApi::Environment.new
|
210
|
+
env.add_component(:god)
|
211
|
+
DNApi::Environment.from(JSON.parse(env.to_json)).components.should_not be_empty
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "#ruby_component" do
|
216
|
+
before(:each) do
|
217
|
+
@env = DNApi::Environment.new
|
218
|
+
end
|
219
|
+
|
220
|
+
DNApi::Components::RubyVersion.all.each do |ruby_component|
|
221
|
+
it "returns #{ruby_component.key_id} when appropriate" do
|
222
|
+
@env.add_component(ruby_component.key_id)
|
223
|
+
@env.ruby_component.should == @env.component(ruby_component.key_id)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe DNApi::Instance do
|
4
|
+
before(:each) do
|
5
|
+
@environment = DNApi::Environment.new
|
6
|
+
@instance = @environment.build_instance(:id => 'default', :role => :app_master)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'has selectors for environment data' do
|
10
|
+
@instance.apps.should == @environment.apps
|
11
|
+
@instance.recipes.should == @environment.recipes
|
12
|
+
@instance.solo?.should == @environment.solo?
|
13
|
+
@instance.ssh_username == @environment.ssh_username
|
14
|
+
@instance.ssh_password == @environment.ssh_password
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has newrelic attributes when it has the :newrelic component" do
|
18
|
+
@instance.add_component(:newrelic, :seed => 'bar', :token => 'sekretz')
|
19
|
+
|
20
|
+
@instance.newrelic_seed.should == "bar"
|
21
|
+
@instance.newrelic_token.should == "sekretz"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "lacks newrelic attributes when it lacks the :newrelic component" do
|
25
|
+
@instance.should_not have_component(:newrelic)
|
26
|
+
@instance.newrelic_token.should be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "allows adding a ssmtp component" do
|
30
|
+
@instance.add_component(:ssmtp)
|
31
|
+
@instance.should have_component(:ssmtp)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows adding a ssmtp component" do
|
35
|
+
@instance.add_component(:exim, :user => "test", :password => "suck", :host => "example.org", :outbound_host => "sendgrid")
|
36
|
+
|
37
|
+
@instance.should have_component(:exim)
|
38
|
+
|
39
|
+
exim = @instance.component(:exim)
|
40
|
+
exim.user.should == "test"
|
41
|
+
exim.password.should == "suck"
|
42
|
+
exim.host.should == "example.org"
|
43
|
+
exim.outbound_host.should == "sendgrid"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'allows adding multiple volume components' do
|
47
|
+
@instance.add_component(:ebs_volume, :mountpoint => '/data', :size => 5 * 1024 * 1024)
|
48
|
+
@instance.add_component(:ebs_volume, :mountpoint => '/db', :size => 5 * 1024 * 1024)
|
49
|
+
|
50
|
+
@instance.volumes.size.should == 2
|
51
|
+
end
|
52
|
+
end
|
data/spec/proxies.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
class Object
|
2
|
+
def deep_clone
|
3
|
+
Marshal.load(Marshal.dump(self))
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module DNApi
|
8
|
+
class ArrayProxy
|
9
|
+
def self.build(array)
|
10
|
+
new(array).build
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(array)
|
14
|
+
@array = array
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
@positions = (0..(@array.size - 1)).to_a
|
19
|
+
@original_array = @array.deep_clone
|
20
|
+
@array.map! do |value|
|
21
|
+
case value
|
22
|
+
when Hash
|
23
|
+
HashProxy.build(value)
|
24
|
+
when Array
|
25
|
+
ArrayProxy.build(value)
|
26
|
+
else
|
27
|
+
value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def index_of(&block)
|
34
|
+
@original_array.each_with_index do |value,i|
|
35
|
+
return i if yield value
|
36
|
+
end
|
37
|
+
raise "Could not find the element in #{inspect}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def size
|
41
|
+
@array.size
|
42
|
+
end
|
43
|
+
|
44
|
+
def empty?
|
45
|
+
@array.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def fully_accessed?
|
49
|
+
@positions.empty? && unaccessed_children.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
def unaccessed
|
53
|
+
{:positions => @positions, :children => unaccessed_children}
|
54
|
+
end
|
55
|
+
|
56
|
+
def unaccessed_children
|
57
|
+
foo = {}
|
58
|
+
@array.each_with_index do |value,i|
|
59
|
+
case value
|
60
|
+
when HashProxy, ArrayProxy
|
61
|
+
foo[i] = value.unaccessed unless value.fully_accessed?
|
62
|
+
when Hash, Array
|
63
|
+
raise "This should be a #{value.class}Proxy: #{value.inspect}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
foo
|
67
|
+
end
|
68
|
+
|
69
|
+
def [](position)
|
70
|
+
@positions.delete(position)
|
71
|
+
@array[position]
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
@array.inspect
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class HashProxy
|
80
|
+
def self.build(hash)
|
81
|
+
new(hash).build
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize(hash)
|
85
|
+
@hash = hash
|
86
|
+
end
|
87
|
+
|
88
|
+
def build
|
89
|
+
@keys = @hash.keys
|
90
|
+
@hash.each do |key,value|
|
91
|
+
case value
|
92
|
+
when Hash
|
93
|
+
@hash[key] = HashProxy.build(value)
|
94
|
+
when Array
|
95
|
+
@hash[key] = ArrayProxy.build(value)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_key?(key)
|
102
|
+
@hash.has_key?(key)
|
103
|
+
end
|
104
|
+
|
105
|
+
def keys
|
106
|
+
@hash.keys
|
107
|
+
end
|
108
|
+
|
109
|
+
def any?
|
110
|
+
@hash.any?
|
111
|
+
end
|
112
|
+
|
113
|
+
def fully_accessed?
|
114
|
+
@keys.empty? && unaccessed_children.empty?
|
115
|
+
end
|
116
|
+
|
117
|
+
def unaccessed
|
118
|
+
{:keys => @keys, :children => unaccessed_children}
|
119
|
+
end
|
120
|
+
|
121
|
+
def unaccessed_children
|
122
|
+
foo = {}
|
123
|
+
@hash.each do |key,value|
|
124
|
+
case value
|
125
|
+
when HashProxy, ArrayProxy
|
126
|
+
foo[key] = value.unaccessed unless value.fully_accessed?
|
127
|
+
when Hash, Array
|
128
|
+
raise "This should be a #{value.class}Proxy: #{key.inspect} #{value.inspect}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
foo
|
132
|
+
end
|
133
|
+
|
134
|
+
def [](key)
|
135
|
+
@keys.delete(key)
|
136
|
+
@hash[key]
|
137
|
+
end
|
138
|
+
|
139
|
+
def inspect
|
140
|
+
@hash.inspect
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/proxies'
|
2
|
+
require 'pp'
|
3
|
+
require 'sexp_processor' #defines Object#deep_copy
|
4
|
+
|
5
|
+
describe DNApi::HashProxy do
|
6
|
+
describe "with no elements" do
|
7
|
+
it "is fully accessed" do
|
8
|
+
DNApi::HashProxy.build({}).should be_fully_accessed
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "with a single element" do
|
13
|
+
before(:each) do
|
14
|
+
@hash = DNApi::HashProxy.build({:foo => :bar})
|
15
|
+
end
|
16
|
+
|
17
|
+
it "is not fully accessed" do
|
18
|
+
@hash.should_not be_fully_accessed
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "which has been accessed" do
|
22
|
+
it "is fully accessed" do
|
23
|
+
@hash[:foo]
|
24
|
+
@hash.should be_fully_accessed
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "with a nested hash" do
|
30
|
+
it "should not be fully accessed unless it's children have been fully accessed" do
|
31
|
+
@hash = DNApi::HashProxy.build({:foo => {:bar => :baz}})
|
32
|
+
lambda { @hash[:foo] }.should_not change { @hash.fully_accessed? }
|
33
|
+
@hash.unaccessed[:keys].should be_empty
|
34
|
+
@hash.unaccessed[:children].should == {:foo => {:keys => [:bar], :children => {}}}
|
35
|
+
@hash[:foo][:bar]
|
36
|
+
@hash.should be_fully_accessed
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "with a nested array" do
|
41
|
+
it "should not be fully accessed until it's elements have been accessed" do
|
42
|
+
@hash = DNApi::HashProxy.build({:foo => [:bar, :baz]})
|
43
|
+
lambda { @hash[:foo] }.should_not change { @hash.fully_accessed? }
|
44
|
+
@hash.unaccessed[:keys].should be_empty
|
45
|
+
@hash.unaccessed[:children].should == {:foo => {:positions => [0, 1], :children => {}}}
|
46
|
+
@hash[:foo][0]
|
47
|
+
@hash[:foo][1]
|
48
|
+
@hash.should be_fully_accessed
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe DNApi::ArrayProxy do
|
54
|
+
describe "with no elements" do
|
55
|
+
it "is fully accessed" do
|
56
|
+
DNApi::ArrayProxy.build([]).should be_fully_accessed
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with a single element" do
|
61
|
+
before(:each) do
|
62
|
+
@array = DNApi::ArrayProxy.build([:foo])
|
63
|
+
end
|
64
|
+
|
65
|
+
it "is not fully accessed" do
|
66
|
+
@array.should_not be_fully_accessed
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "which has been accessed" do
|
70
|
+
it "is fully accessed" do
|
71
|
+
@array[0]
|
72
|
+
@array.should be_fully_accessed
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|