simply_stored 0.1.4
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.
- data/lib/simply_stored/class_methods_base.rb +31 -0
- data/lib/simply_stored/couch/belongs_to.rb +117 -0
- data/lib/simply_stored/couch/ext/couch_potato.rb +16 -0
- data/lib/simply_stored/couch/has_many.rb +148 -0
- data/lib/simply_stored/couch/has_one.rb +93 -0
- data/lib/simply_stored/couch/validations.rb +74 -0
- data/lib/simply_stored/couch/views/array_property_view_spec.rb +22 -0
- data/lib/simply_stored/couch/views.rb +1 -0
- data/lib/simply_stored/couch.rb +278 -0
- data/lib/simply_stored/instance_methods.rb +143 -0
- data/lib/simply_stored/simpledb/associations.rb +196 -0
- data/lib/simply_stored/simpledb/attributes.rb +173 -0
- data/lib/simply_stored/simpledb/storag.rb +85 -0
- data/lib/simply_stored/simpledb/validations.rb +88 -0
- data/lib/simply_stored/simpledb.rb +212 -0
- data/lib/simply_stored/storage.rb +93 -0
- data/lib/simply_stored.rb +9 -0
- data/test/custom_views_test.rb +33 -0
- data/test/fixtures/couch.rb +182 -0
- data/test/fixtures/simpledb/item.rb +11 -0
- data/test/fixtures/simpledb/item_daddy.rb +8 -0
- data/test/fixtures/simpledb/log_item.rb +3 -0
- data/test/fixtures/simpledb/namespace_bar.rb +5 -0
- data/test/fixtures/simpledb/namespace_foo.rb +7 -0
- data/test/fixtures/simpledb/protected_item.rb +3 -0
- data/test/simply_stored_couch_test.rb +1684 -0
- data/test/simply_stored_simpledb_test.rb +1341 -0
- data/test/test_helper.rb +22 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/dot/dot.rb +29 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/evaluator/evaluator.rb +133 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/closure_hash.rb +15 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar.rb +240 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar_symbol.rb +27 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/precedence.rb +19 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/production.rb +36 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/accept_actions.rb +36 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/alphabet.rb +21 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/compiled_lexer.rb +46 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/dfa.rb +121 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexeme.rb +32 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer.rb +70 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer_run.rb +78 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_grammar.rb +392 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_parser.rb +2010 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_tokenizer.rb +14 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/specification.rb +96 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state.rb +68 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state_machine.rb +37 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/action.rb +55 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/channel.rb +58 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/compiled_parser.rb +51 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/conflict.rb +54 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/item.rb +42 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_result.rb +50 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_tree.rb +66 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser.rb +165 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_methods.rb +11 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_run.rb +39 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_state.rb +74 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/token.rb +22 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/runtime.rb +51 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka/tokenizer/tokenizer.rb +190 -0
- data/test/vendor/dhaka-2.2.1/lib/dhaka.rb +62 -0
- data/test/vendor/dhaka-2.2.1/test/all_tests.rb +5 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator.rb +64 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator_test.rb +43 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar.rb +41 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar_test.rb +9 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_test_methods.rb +9 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer.rb +39 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer_test.rb +38 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_evaluator.rb +43 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +24 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar_test.rb +30 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_lexer_specification.rb +23 -0
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +33 -0
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_grammar.rb +23 -0
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_tokenizer.rb +22 -0
- data/test/vendor/dhaka-2.2.1/test/brackets/brackets_test.rb +28 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver.rb +46 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver_test.rb +276 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator.rb +284 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator_test.rb +38 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_grammar.rb +104 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer.rb +109 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_specification.rb +37 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_test.rb +58 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser.rb +879 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser_test.rb +55 -0
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_test.rb +170 -0
- data/test/vendor/dhaka-2.2.1/test/core/another_lalr_but_not_slr_grammar.rb +20 -0
- data/test/vendor/dhaka-2.2.1/test/core/compiled_parser_test.rb +44 -0
- data/test/vendor/dhaka-2.2.1/test/core/dfa_test.rb +170 -0
- data/test/vendor/dhaka-2.2.1/test/core/evaluator_test.rb +22 -0
- data/test/vendor/dhaka-2.2.1/test/core/grammar_test.rb +83 -0
- data/test/vendor/dhaka-2.2.1/test/core/lalr_but_not_slr_grammar.rb +19 -0
- data/test/vendor/dhaka-2.2.1/test/core/lexer_test.rb +139 -0
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar.rb +7 -0
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar_test.rb +8 -0
- data/test/vendor/dhaka-2.2.1/test/core/nullable_grammar.rb +21 -0
- data/test/vendor/dhaka-2.2.1/test/core/parse_result_test.rb +44 -0
- data/test/vendor/dhaka-2.2.1/test/core/parser_state_test.rb +24 -0
- data/test/vendor/dhaka-2.2.1/test/core/parser_test.rb +131 -0
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar.rb +17 -0
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar_test.rb +9 -0
- data/test/vendor/dhaka-2.2.1/test/core/rr_conflict_grammar.rb +21 -0
- data/test/vendor/dhaka-2.2.1/test/core/simple_grammar.rb +22 -0
- data/test/vendor/dhaka-2.2.1/test/core/sr_conflict_grammar.rb +16 -0
- data/test/vendor/dhaka-2.2.1/test/dhaka_test_helper.rb +17 -0
- data/test/vendor/dhaka-2.2.1/test/fake_logger.rb +17 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb/client_exception.rb +10 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb/db.rb +146 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb/query_language.rb +266 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb/server.rb +33 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb/servlet.rb +191 -0
- data/test/vendor/simplerdb-0.2/lib/simplerdb.rb +3 -0
- data/test/vendor/simplerdb-0.2/test/functional_test.rb +81 -0
- data/test/vendor/simplerdb-0.2/test/query_evaluator_test.rb +73 -0
- data/test/vendor/simplerdb-0.2/test/query_parser_test.rb +64 -0
- data/test/vendor/simplerdb-0.2/test/simplerdb_test.rb +80 -0
- metadata +182 -0
|
@@ -0,0 +1,1341 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
|
2
|
+
|
|
3
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/item_daddy.rb"
|
|
4
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/item.rb"
|
|
5
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/namespace_foo.rb"
|
|
6
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/namespace_bar.rb"
|
|
7
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/log_item.rb"
|
|
8
|
+
require File.dirname(__FILE__) + "/fixtures/simpledb/protected_item.rb"
|
|
9
|
+
|
|
10
|
+
require 'simplerdb/server'
|
|
11
|
+
old_stderr = $stderr
|
|
12
|
+
$stderr = File.open("/dev/null","w")
|
|
13
|
+
|
|
14
|
+
$server ||= SimplerDB::Server.new(8087)
|
|
15
|
+
$thread ||= Thread.new do
|
|
16
|
+
trap(:INT) {
|
|
17
|
+
exit
|
|
18
|
+
}
|
|
19
|
+
$server.start
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
$stderr = old_stderr
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SimplyStoredTest < Test::Unit::TestCase
|
|
26
|
+
context "The simply stored base class" do
|
|
27
|
+
setup do
|
|
28
|
+
ItemDaddy.instance_eval do
|
|
29
|
+
has_one :item, :clear => :nullify
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ItemDaddy.instance_eval do
|
|
33
|
+
has_many :items, :clear => :nullify
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
SimplyStored::Simple.aws_access_key = 'foo'
|
|
37
|
+
SimplyStored::Simple.aws_secret_access_key = 'bar'
|
|
38
|
+
RightAws::ActiveSdb.establish_connection(SimplyStored::Simple.aws_access_key, SimplyStored::Simple.aws_secret_access_key, :server => 'localhost', :port => '8087', :protocol => 'http', :logger => Logger.new('/dev/null'))
|
|
39
|
+
Item.create_domain
|
|
40
|
+
ItemDaddy.create_domain
|
|
41
|
+
Namespace::Foo.create_domain
|
|
42
|
+
Namespace::Bar.create_domain
|
|
43
|
+
LogItem.create_domain
|
|
44
|
+
ProtectedItem.create_domain
|
|
45
|
+
@item = Item.new(:foo_attribute => 'a', :bar_attribute_array => 'goog')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
should "use UUIDTools::UUID.random_create to generate the ID" do
|
|
49
|
+
UUIDTools::UUID.expects(:random_create).returns('foo')
|
|
50
|
+
item = create_item
|
|
51
|
+
assert_equal 'foo', item.id
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "attributes" do
|
|
55
|
+
should "set the given attributes" do
|
|
56
|
+
@item.set_attributes(:foo_attribute => '99', :bar_attribute_array => ['1', '2', '3'])
|
|
57
|
+
assert_equal '99', @item.foo_attribute
|
|
58
|
+
assert_equal ['1', '2', '3'], @item.bar_attribute_array
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
should "not nil the other attributes" do
|
|
62
|
+
@item.bar_attribute_array = ['99']
|
|
63
|
+
@item.set_attributes(:foo_attribute => '66')
|
|
64
|
+
assert_equal ['99'], @item.bar_attribute_array
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context "simpledb getter and setter" do
|
|
69
|
+
|
|
70
|
+
should "simpledb_attribute_getter" do
|
|
71
|
+
assert @item.respond_to?(:foo_attribute)
|
|
72
|
+
@item['foo_attribute'] = 'abc'
|
|
73
|
+
assert_equal 'abc', @item.foo_attribute
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
should "getter when empty" do
|
|
77
|
+
@item['bar_attribute_array'] = []
|
|
78
|
+
@item['foo_attribute'] = nil
|
|
79
|
+
assert_equal nil, @item.foo_attribute
|
|
80
|
+
assert_equal [], @item.bar_attribute_array
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
should "getter when empty after setting using the setter" do
|
|
84
|
+
@item.bar_attribute_array = []
|
|
85
|
+
@item.foo_attribute = nil
|
|
86
|
+
assert_equal nil, @item.foo_attribute
|
|
87
|
+
assert_equal [], @item.bar_attribute_array
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
should "attribute setter" do
|
|
91
|
+
assert @item.respond_to?(:foo_attribute=)
|
|
92
|
+
@item.foo_attribute = 5
|
|
93
|
+
assert_equal 5, @item['foo_attribute'].first
|
|
94
|
+
assert_equal 5, @item.foo_attribute
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
should "attribute definition adds to internal attribute list" do
|
|
98
|
+
['foo_attribute', 'item_daddy_id', 'bar_attribute_array', 'updated_at', 'created_at', 'integer_field'].each do |attr|
|
|
99
|
+
assert @item.class.instance_variable_get("@_defined_attributes").include?(attr)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
should "array_getter" do
|
|
104
|
+
assert @item.respond_to?(:bar_attribute_array)
|
|
105
|
+
@item['bar_attribute_array'] = ['abc']
|
|
106
|
+
assert_equal ['abc'], @item.bar_attribute_array
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
should "array_setter" do
|
|
110
|
+
assert @item.respond_to?(:bar_attribute_array=)
|
|
111
|
+
@item.bar_attribute_array = 5
|
|
112
|
+
assert_equal [5], @item['bar_attribute_array']
|
|
113
|
+
assert_equal [5], @item.bar_attribute_array
|
|
114
|
+
|
|
115
|
+
@item.bar_attribute_array << 7
|
|
116
|
+
assert_equal [5,7], @item.bar_attribute_array
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
should "array_setter when given an empty array" do
|
|
120
|
+
@item['bar_attribute_array'] = []
|
|
121
|
+
assert_equal [], @item.bar_attribute_array
|
|
122
|
+
@item.bar_attribute_array << 7
|
|
123
|
+
assert_equal [7], @item.bar_attribute_array
|
|
124
|
+
|
|
125
|
+
@item.bar_attribute_array << 11
|
|
126
|
+
assert_equal [7,11], @item.bar_attribute_array
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context "belongs_to" do
|
|
130
|
+
|
|
131
|
+
should "getter" do
|
|
132
|
+
assert @item.respond_to?(:item_daddy)
|
|
133
|
+
|
|
134
|
+
@item[:item_daddy_id] = 'the_id_of_item'
|
|
135
|
+
ItemDaddy.expects(:find).with('the_id_of_item', {:auto_load => true}).returns('foo')
|
|
136
|
+
assert_equal 'foo', @item.item_daddy
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
should "setter" do
|
|
140
|
+
assert @item.respond_to?(:item_daddy=)
|
|
141
|
+
|
|
142
|
+
item_daddy = ItemDaddy.new
|
|
143
|
+
item_daddy.expects(:id).returns('item_daddy_id')
|
|
144
|
+
@item.expects(:item_daddy_id=).with('item_daddy_id')
|
|
145
|
+
|
|
146
|
+
@item.item_daddy = item_daddy
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
should "check the class when setting" do
|
|
150
|
+
assert_raise(ArgumentError, 'expected ItemDaddy got String') do
|
|
151
|
+
@item.item_daddy = 'foo'
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
should "use the cache when getting" do
|
|
156
|
+
@item[:item_daddy_id] = 'the_id_of_item'
|
|
157
|
+
ItemDaddy.expects(:find).with('the_id_of_item', {:auto_load => true}).returns('foo').times(1)
|
|
158
|
+
assert_equal 'foo', @item.item_daddy
|
|
159
|
+
assert_equal 'foo', @item.item_daddy
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
should "set the cache when setting" do
|
|
163
|
+
@item.instance_variable_set("@_cached_belongs_to_item_daddy", 'foo')
|
|
164
|
+
assert_equal 'foo', @item.item_daddy
|
|
165
|
+
|
|
166
|
+
daddy = ItemDaddy.new('id' => 'the-ID')
|
|
167
|
+
@item.expects(:item_daddy_id=)
|
|
168
|
+
@item.expects(:instance_variable_set).with("@_cached_belongs_to_item_daddy", daddy)
|
|
169
|
+
|
|
170
|
+
@item.item_daddy = daddy
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
should "not hit the database when the id column is empty" do
|
|
174
|
+
ItemDaddy.expects(:find).never
|
|
175
|
+
@item.item_daddy_id = []
|
|
176
|
+
@item.item_daddy
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
context "has_one" do
|
|
181
|
+
|
|
182
|
+
should "getter" do
|
|
183
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
184
|
+
assert daddy.respond_to?(:item)
|
|
185
|
+
|
|
186
|
+
Item.expects(:send).with(:find_by_item_daddy_id, 'daddy_id', {:auto_load => true}).returns('a')
|
|
187
|
+
assert_equal 'a', daddy.item
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
should "use the cache when getting" do
|
|
191
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
192
|
+
Item.expects(:send).with(:find_by_item_daddy_id, 'daddy_id', {:auto_load => true}).returns('a').times(1)
|
|
193
|
+
assert_equal 'a', daddy.item
|
|
194
|
+
assert_equal 'a', daddy.item
|
|
195
|
+
assert_equal 'a', daddy.item
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
should "setter" do
|
|
199
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
200
|
+
assert daddy.respond_to?(:item=)
|
|
201
|
+
item = Item.new
|
|
202
|
+
item.expects(:item_daddy_id=).with('daddy_id')
|
|
203
|
+
|
|
204
|
+
daddy.item = item
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
should "set the cache when setting" do
|
|
208
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
209
|
+
item = Item.new
|
|
210
|
+
item.expects(:item_daddy_id=).with('daddy_id')
|
|
211
|
+
|
|
212
|
+
daddy.item = item
|
|
213
|
+
assert_equal item, daddy.instance_variable_get("@_cached_has_one_item")
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
should "check the class" do
|
|
217
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
218
|
+
assert_raise(ArgumentError, 'expected Item got String') do
|
|
219
|
+
daddy.item = 'foo'
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
should "clear relations when depending:nullify" do
|
|
224
|
+
|
|
225
|
+
ItemDaddy.instance_eval do
|
|
226
|
+
has_one :item, :clear => :nullify
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
230
|
+
old_item = Item.new
|
|
231
|
+
old_item.stubs(:item_daddy_id).returns('daddy_id')
|
|
232
|
+
old_item.expects(:item_daddy_id=).with(nil)
|
|
233
|
+
daddy.expects(:item).returns(old_item)
|
|
234
|
+
|
|
235
|
+
new_item = Item.new
|
|
236
|
+
new_item.stubs(:item_daddy_id=)
|
|
237
|
+
daddy.item = new_item
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
should "delete relations when depending:destroy" do
|
|
241
|
+
|
|
242
|
+
ItemDaddy.instance_eval do
|
|
243
|
+
has_one :item, :clear => :destroy
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
247
|
+
old_item = Item.new
|
|
248
|
+
old_item.stubs(:item_daddy_id).returns('daddy_id')
|
|
249
|
+
old_item.expects(:delete)
|
|
250
|
+
daddy.expects(:item).returns(old_item)
|
|
251
|
+
|
|
252
|
+
new_item = Item.new
|
|
253
|
+
new_item.stubs(:item_daddy_id=)
|
|
254
|
+
daddy.item = new_item
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
context "has_many" do
|
|
260
|
+
|
|
261
|
+
should "getter" do
|
|
262
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
263
|
+
assert daddy.respond_to?(:items)
|
|
264
|
+
|
|
265
|
+
Item.expects(:send).with(:find_all_by_item_daddy_id, 'daddy_id', {:auto_load => true}).returns(['a'])
|
|
266
|
+
assert_equal ['a'], daddy.items
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
should "use the cache when getting" do
|
|
270
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
271
|
+
Item.expects(:send).with(:find_all_by_item_daddy_id, 'daddy_id', {:auto_load => true}).returns(['a']).times(1)
|
|
272
|
+
assert_equal ['a'], daddy.items
|
|
273
|
+
assert_equal ['a'], daddy.items
|
|
274
|
+
assert_equal ['a'], daddy.items
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
should "setter" do
|
|
278
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
279
|
+
assert daddy.respond_to?(:add_item)
|
|
280
|
+
assert daddy.respond_to?(:remove_item)
|
|
281
|
+
assert daddy.respond_to?(:remove_all_items)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
should "add_item" do
|
|
285
|
+
daddy = ItemDaddy.new(:id => 'daddy_id_')
|
|
286
|
+
item = Item.new
|
|
287
|
+
item.expects(:item_daddy_id=).with('daddy_id_')
|
|
288
|
+
|
|
289
|
+
assert_equal [], daddy.items
|
|
290
|
+
daddy.add_item(item)
|
|
291
|
+
assert_equal [item], daddy.items
|
|
292
|
+
assert_equal [item], daddy.instance_variable_get("@_cached_has_many_items")
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
should "check the class when using add_item" do
|
|
296
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
297
|
+
assert_raise(ArgumentError, 'excepted Item got String') do
|
|
298
|
+
daddy.add_item('foo')
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
should "save when adding an item" do
|
|
303
|
+
@item.expects(:save).with(false)
|
|
304
|
+
|
|
305
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
306
|
+
daddy.add_item(@item)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
should "remove an item" do
|
|
310
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
311
|
+
item = Item.new
|
|
312
|
+
item.expects(:item_daddy_id=).with(nil)
|
|
313
|
+
item.expects(:item_daddy_id).returns('daddy_id')
|
|
314
|
+
item.expects(:save).with(false)
|
|
315
|
+
|
|
316
|
+
daddy.instance_variable_set("@_cached_has_many_items", [item])
|
|
317
|
+
assert_equal [item], daddy.items
|
|
318
|
+
daddy.remove_item(item)
|
|
319
|
+
assert_equal [], daddy.items
|
|
320
|
+
assert_equal [], daddy.instance_variable_get("@_cached_has_many_items")
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
should "check ownership when removing" do
|
|
324
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
325
|
+
my_item = Item.new
|
|
326
|
+
my_item.expects(:item_daddy_id=).with(nil)
|
|
327
|
+
my_item.expects(:item_daddy_id).returns('daddy_id')
|
|
328
|
+
|
|
329
|
+
other_item = Item.new
|
|
330
|
+
other_item.expects(:item_daddy_id=).never
|
|
331
|
+
other_item.expects(:item_daddy_id).returns('not_daddys_id')
|
|
332
|
+
|
|
333
|
+
assert_nothing_raised do
|
|
334
|
+
daddy.remove_item(my_item)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
assert_raise(ArgumentError, 'cannot remove as not mine') do
|
|
338
|
+
daddy.remove_item(other_item)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
should "remove all items" do
|
|
343
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
344
|
+
item_1 = Item.new
|
|
345
|
+
item_1.expects(:item_daddy_id).returns('daddy_id')
|
|
346
|
+
item_1.expects(:item_daddy_id=).with(nil)
|
|
347
|
+
item_2 = Item.new
|
|
348
|
+
item_2.expects(:item_daddy_id).returns('daddy_id')
|
|
349
|
+
item_2.expects(:item_daddy_id=).with(nil)
|
|
350
|
+
|
|
351
|
+
Item.expects(:find_all_by_item_daddy_id).with('daddy_id').returns([item_1, item_2])
|
|
352
|
+
|
|
353
|
+
daddy.remove_all_items
|
|
354
|
+
|
|
355
|
+
assert_equal [], daddy.instance_variable_get("@_cached_has_many_items")
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
should "delete relations if depending:destroy" do
|
|
359
|
+
ItemDaddy.instance_eval do
|
|
360
|
+
has_many :items, :clear => :destroy
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
daddy = ItemDaddy.new(:id => 'daddy_id')
|
|
364
|
+
item = Item.new
|
|
365
|
+
item.expects(:delete)
|
|
366
|
+
item.expects(:item_daddy_id).returns('daddy_id')
|
|
367
|
+
|
|
368
|
+
daddy.remove_item(item)
|
|
369
|
+
assert_equal [], daddy.instance_variable_get("@_cached_has_many_items")
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
context "attribute proctection against mass assignment" do
|
|
376
|
+
|
|
377
|
+
context "when using attr_protected" do
|
|
378
|
+
setup do
|
|
379
|
+
ProtectedItem.instance_eval do
|
|
380
|
+
@_accessible_attributes ||= []
|
|
381
|
+
attr_protected :a, :b
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
should "not allow to set with mass assignment using attributes=" do
|
|
386
|
+
item = ProtectedItem.new
|
|
387
|
+
item.attributes = {:a => 'a', :c => 'c'}
|
|
388
|
+
assert_equal 'c', item.c
|
|
389
|
+
assert_nil item.a
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
should "not allow to set with mass assignment using attributes= - ignore string vs. symbol" do
|
|
393
|
+
item = ProtectedItem.new
|
|
394
|
+
item.attributes = {'a' => 'a', 'c' => 'c'}
|
|
395
|
+
assert_equal 'c', item.c
|
|
396
|
+
assert_nil item.a
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
should "not allow to set with mass assignment using the constructor" do
|
|
400
|
+
item = ProtectedItem.new(:a => 'a', :c => 'c')
|
|
401
|
+
assert_equal 'c', item.c
|
|
402
|
+
assert_nil item.a
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
should "allow to set with mass assignment using update_attributes" do
|
|
406
|
+
item = ProtectedItem.new
|
|
407
|
+
item.update_attributes(:a => 'a', :c => 'c')
|
|
408
|
+
item.reload
|
|
409
|
+
assert_equal 'c', item.c
|
|
410
|
+
assert_equal 'a', item.a
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
should "not allow to set with mass assignment using set_attributes" do
|
|
414
|
+
item = ProtectedItem.new
|
|
415
|
+
item.set_attributes(:a => 'a', :c => 'c')
|
|
416
|
+
assert_equal 'c', item.c
|
|
417
|
+
assert_nil item.a
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
context "attr_accessible" do
|
|
422
|
+
setup do
|
|
423
|
+
ProtectedItem.instance_eval do
|
|
424
|
+
@_protected_attributes ||= []
|
|
425
|
+
attr_accessible :c
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
should "not allow to set with mass assignment using attributes=" do
|
|
430
|
+
item = ProtectedItem.new
|
|
431
|
+
item.attributes = {:a => 'a', :c => 'c'}
|
|
432
|
+
assert_equal 'c', item.c
|
|
433
|
+
assert_nil item.a
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
should "not allow to set with mass assignment using the constructor" do
|
|
437
|
+
item = ProtectedItem.new(:a => 'a', :c => 'c')
|
|
438
|
+
assert_equal 'c', item.c
|
|
439
|
+
assert_nil item.a
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
should "allow to set with mass assignment using update_attributes" do
|
|
443
|
+
item = ProtectedItem.new
|
|
444
|
+
item.update_attributes(:a => 'a', :c => 'c')
|
|
445
|
+
item.reload
|
|
446
|
+
assert_equal 'c', item.c
|
|
447
|
+
assert_equal 'a', item.a
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
should "not allow to set with mass assignment using set_attributes" do
|
|
451
|
+
item = ProtectedItem.new
|
|
452
|
+
item.set_attributes(:a => 'a', :c => 'c')
|
|
453
|
+
assert_equal 'c', item.c
|
|
454
|
+
assert_nil item.a
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
context "validation and errors" do
|
|
461
|
+
|
|
462
|
+
should "get errors" do
|
|
463
|
+
@item.send(:add_error, 'foo', 'bar')
|
|
464
|
+
assert_equal [['foo', 'bar']], @item.errors
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
should "valid_if_errors" do
|
|
468
|
+
@item.stubs(:errors).returns([true])
|
|
469
|
+
assert !@item.valid?
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
should "valid_if_no_errors" do
|
|
473
|
+
@item.stubs(:errors).returns([])
|
|
474
|
+
assert @item.valid?
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
should "clear_errors" do
|
|
478
|
+
@item.instance_variable_set("@errors", [true])
|
|
479
|
+
assert !@item.errors.blank?
|
|
480
|
+
@item.send(:clear_errors)
|
|
481
|
+
assert @item.errors.blank?
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
should "valid_calls_validate" do
|
|
485
|
+
@item.expects(:validate)
|
|
486
|
+
@item.valid?
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
should "clears errors on a successful save" do
|
|
490
|
+
@item.expects(:valid?).returns(true)
|
|
491
|
+
@item.expects(:active_sdb_save).returns(true)
|
|
492
|
+
@item.expects(:clear_errors).returns(true)
|
|
493
|
+
|
|
494
|
+
assert @item.save
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
should "clear errors when valid? is true" do
|
|
498
|
+
@item.instance_variable_set("@errors", [:am, 'foo'])
|
|
499
|
+
assert @item.valid?
|
|
500
|
+
assert_equal [], @item.instance_variable_get("@errors")
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
should "fail the save when there's validation errors" do
|
|
504
|
+
@item.instance_variable_set("@errors", [:am, 'foo'])
|
|
505
|
+
@item.stubs(:clear_errors)
|
|
506
|
+
@item.expects(:active_sdb_save).never
|
|
507
|
+
assert !@item.save
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
should "more_attributes_than_allowed" do
|
|
511
|
+
@item[:foo] = 'bar'
|
|
512
|
+
assert !@item.valid?, @item.attributes.inspect
|
|
513
|
+
assert_equal [['foo', 'is unknown and should not be set']], @item.errors
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
should "more_attributes_than_allowed_does_not_check_id" do
|
|
517
|
+
@item[:id] = '90834980324kjndfaslkjadsfp89'
|
|
518
|
+
assert @item.valid?, @item.errors.inspect
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
should "require presence" do
|
|
522
|
+
daddy = ItemDaddy.new
|
|
523
|
+
daddy.name = nil
|
|
524
|
+
assert !daddy.valid?
|
|
525
|
+
|
|
526
|
+
daddy.name = 'foo'
|
|
527
|
+
assert daddy.valid?
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
should "require inclusion" do
|
|
531
|
+
@item.foo_attribute = 'd'
|
|
532
|
+
assert !@item.valid?
|
|
533
|
+
|
|
534
|
+
@item.foo_attribute = 'c'
|
|
535
|
+
assert @item.valid?
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
should "require inclusion and allow_blank" do
|
|
539
|
+
Item.instance_eval do
|
|
540
|
+
require_inclusion_of :foo_attribute, ['a', 'b', 'c'], :allow_blank => true
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
item = Item.new(:bar_attribute_array => ['goog'], :foo_attribute => nil)
|
|
544
|
+
assert item.valid?, item.errors.inspect
|
|
545
|
+
|
|
546
|
+
Item.instance_eval do
|
|
547
|
+
require_inclusion_of :foo_attribute, ['a', 'b', 'c'], :allow_blank => false
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
assert !item.valid?, item.errors.inspect
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
should "require inclusion should work with array attribute" do
|
|
554
|
+
@item.bar_attribute_array = ['foobar']
|
|
555
|
+
assert !@item.valid?
|
|
556
|
+
|
|
557
|
+
@item.bar_attribute_array = ['goog']
|
|
558
|
+
assert @item.valid?
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
should "save_without_validations" do
|
|
562
|
+
item = Item.new(:foo_attribute => nil)
|
|
563
|
+
assert !item.valid?
|
|
564
|
+
assert !item.save
|
|
565
|
+
assert item.save(false)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
context "format validation" do
|
|
569
|
+
setup do
|
|
570
|
+
Item.instance_eval do
|
|
571
|
+
simpledb_string :format_attribute
|
|
572
|
+
require_format_of :format_attribute, /a/, :allow_blank => true
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
@item = Item.new(:format_attribute => nil, :foo_attribute => 'a')
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
should "check format" do
|
|
579
|
+
@item.format_attribute = 'b'
|
|
580
|
+
assert !@item.valid?
|
|
581
|
+
|
|
582
|
+
@item.format_attribute = 'c'
|
|
583
|
+
assert !@item.valid?
|
|
584
|
+
|
|
585
|
+
@item.format_attribute = 'a'
|
|
586
|
+
assert @item.valid?, @item.errors.inspect
|
|
587
|
+
|
|
588
|
+
@item.format_attribute = 'abc'
|
|
589
|
+
assert @item.valid?
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
should "allow nil" do
|
|
593
|
+
@item.format_attribute = 'b'
|
|
594
|
+
assert !@item.valid?
|
|
595
|
+
|
|
596
|
+
@item.format_attribute = nil
|
|
597
|
+
assert @item.valid?, @item.errors.inspect
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
context "callbacks" do
|
|
604
|
+
|
|
605
|
+
should "not call callbacks if none are defined" do
|
|
606
|
+
@item.stubs(:active_sdb_save).returns(true)
|
|
607
|
+
@item.stubs(:respond_to?).returns(false)
|
|
608
|
+
|
|
609
|
+
@item.expects(:before_save).never
|
|
610
|
+
@item.expects(:after_save).never
|
|
611
|
+
assert @item.save
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
should "call callbacks if none are defined" do
|
|
615
|
+
@item.stubs(:active_sdb_save).returns(true)
|
|
616
|
+
|
|
617
|
+
def @item.before_save
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def @item.after_save
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
@item.expects(:before_save)
|
|
624
|
+
@item.expects(:after_save)
|
|
625
|
+
assert @item.save
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
should "call after_create and before_create callbacks if we have a new record" do
|
|
629
|
+
@item.stubs(:new_record?).returns(true)
|
|
630
|
+
@item.stubs(:active_sdb_save).returns(true)
|
|
631
|
+
|
|
632
|
+
def @item.before_create
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def @item.after_create
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
@item.expects(:before_create)
|
|
639
|
+
@item.expects(:after_create)
|
|
640
|
+
assert @item.save
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
should "not call after_create and before_create callbacks if we have an existing record" do
|
|
644
|
+
@item.stubs(:new_record?).returns(false)
|
|
645
|
+
@item.stubs(:active_sdb_save).returns(true)
|
|
646
|
+
|
|
647
|
+
def @item.before_create
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
def @item.after_create
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
@item.expects(:before_create).never
|
|
654
|
+
@item.expects(:after_create).never
|
|
655
|
+
assert @item.save
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
should "call before_validation before the validation if defined" do
|
|
659
|
+
def @item.before_validation
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
@item.expects(:before_validation)
|
|
663
|
+
assert @item.valid?
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
should "not call before_validation if it is not defined" do
|
|
667
|
+
@item.expects(:respond_to?).with(:before_validation).returns(false)
|
|
668
|
+
|
|
669
|
+
@item.expects(:before_validation).never
|
|
670
|
+
assert @item.valid?
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
should "before_delete_callback_not_called_if_no_before_delete_defined" do
|
|
674
|
+
@item.id = '5'
|
|
675
|
+
assert !@item.respond_to?(:before_delete)
|
|
676
|
+
assert_nothing_raised do
|
|
677
|
+
@item.delete
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
should "before_delete_callback" do
|
|
682
|
+
item_with_callback = Item.create(:foo_attribute => '123', :bar_attribute_array => ['a', 'b'], :id => 5)
|
|
683
|
+
def item_with_callback.before_delete
|
|
684
|
+
'foo'
|
|
685
|
+
end
|
|
686
|
+
item_with_callback.expects(:before_delete)
|
|
687
|
+
|
|
688
|
+
item_with_callback.delete
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
should "after_delete_callback_not_called_if_no_before_delete_defined" do
|
|
692
|
+
@item.id = '5'
|
|
693
|
+
assert !@item.respond_to?(:after_delete)
|
|
694
|
+
assert_nothing_raised do
|
|
695
|
+
@item.delete
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
should "after_delete_callback" do
|
|
700
|
+
item_with_callback = Item.create(:foo_attribute => '123', :bar_attribute_array => ['a', 'b'], :id => 5)
|
|
701
|
+
def item_with_callback.after_delete
|
|
702
|
+
'foo'
|
|
703
|
+
end
|
|
704
|
+
item_with_callback.expects(:after_delete)
|
|
705
|
+
|
|
706
|
+
item_with_callback.delete
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
context "timestamps" do
|
|
712
|
+
|
|
713
|
+
should "timestamps" do
|
|
714
|
+
item = Item.new(:foo_attribute => 'a', :bar_attribute_array => ['goog'])
|
|
715
|
+
assert_nil item.updated_at
|
|
716
|
+
assert_nil item.created_at
|
|
717
|
+
|
|
718
|
+
time = Time.now
|
|
719
|
+
Time.stubs(:now).returns(time)
|
|
720
|
+
|
|
721
|
+
assert item.save, item.errors.inspect
|
|
722
|
+
assert_equal time.utc, item.updated_at
|
|
723
|
+
assert_equal time.utc, item.created_at
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
should "timestamps_when_saving_without_validations" do
|
|
727
|
+
item = Item.new(:foo_attribute => 'a', :bar_attribute_array => ['goog'])
|
|
728
|
+
assert_nil item.updated_at
|
|
729
|
+
assert_nil item.created_at
|
|
730
|
+
|
|
731
|
+
time = Time.now
|
|
732
|
+
Time.stubs(:now).returns(time)
|
|
733
|
+
|
|
734
|
+
assert item.save(false)
|
|
735
|
+
assert_equal time.utc, item.updated_at
|
|
736
|
+
assert_equal time.utc, item.created_at
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
should "timestamps_when_existing_record" do
|
|
740
|
+
item = Item.new(:foo_attribute => 'a', :bar_attribute_array => ['goog'])
|
|
741
|
+
item.stubs(:new_record?).returns(false)
|
|
742
|
+
assert_nil item.updated_at
|
|
743
|
+
assert_nil item.created_at
|
|
744
|
+
|
|
745
|
+
time = Time.now
|
|
746
|
+
Time.stubs(:now).returns(time)
|
|
747
|
+
|
|
748
|
+
assert item.save, item.errors.inspect
|
|
749
|
+
assert_equal time.utc, item.updated_at
|
|
750
|
+
assert_nil item.created_at
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
should "timestamps_when_validation_fails" do
|
|
754
|
+
item = Item.new(:foo_attribute => 'a', :bar_attribute_array => ['goog'])
|
|
755
|
+
item.expects(:valid?).returns(false)
|
|
756
|
+
assert_nil item.updated_at
|
|
757
|
+
assert_nil item.created_at
|
|
758
|
+
|
|
759
|
+
assert !item.save
|
|
760
|
+
assert_nil item.updated_at
|
|
761
|
+
assert_nil item.created_at
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
should "timestamp_saves_representation_as_integer" do
|
|
765
|
+
@item.updated_at = Time.local(2004, 1, 12, 9, 7, 59)
|
|
766
|
+
assert_equal ['20040112090759'], @item['updated_at']
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
should "timestamp_loads_from_representation_as_integer" do
|
|
770
|
+
@item['updated_at'] = '20040112090759'
|
|
771
|
+
assert_equal Time.local(2004, 1, 12, 10, 7, 59).utc, @item.updated_at
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
context "integer attributes" do
|
|
776
|
+
|
|
777
|
+
should "save representation padded" do
|
|
778
|
+
@item.integer_field = 91
|
|
779
|
+
assert_equal ['0000000000000091'], @item['integer_field']
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
should "load representation padded" do
|
|
783
|
+
@item['integer_field'] = '0000000000000091'
|
|
784
|
+
assert_equal 91, @item.integer_field
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
context "find helper" do
|
|
789
|
+
|
|
790
|
+
should "all" do
|
|
791
|
+
Item.expects(:find).with(:all, instance_of(Hash))
|
|
792
|
+
Item.all
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
should "all_with_options" do
|
|
796
|
+
Item.expects(:find).with do |arg, options|
|
|
797
|
+
arg == :all && options[:auto_load] == true
|
|
798
|
+
end
|
|
799
|
+
Item.all(:auto_load => true)
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
should "first" do
|
|
803
|
+
Item.expects(:find).with(:first, instance_of(Hash))
|
|
804
|
+
Item.first
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
should "first_with_options" do
|
|
808
|
+
Item.expects(:find).with do |arg, options|
|
|
809
|
+
arg == :first && options[:auto_load] == true
|
|
810
|
+
end
|
|
811
|
+
Item.first(:auto_load => true)
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
context "when finding an object" do
|
|
816
|
+
should "should raise RecordNotFound when the object wasn't found" do
|
|
817
|
+
assert_raise(SimplyStored::RecordNotFound) do
|
|
818
|
+
Item.find("bla")
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
should "should retry x-times before raising RecordNotFound" do
|
|
823
|
+
item = create_item
|
|
824
|
+
seq = sequence("find")
|
|
825
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).raises(RightAws::ActiveSdb::ActiveSdbError, "Couldn't find Item with ID").in_sequence(seq)
|
|
826
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).returns([item]).in_sequence(seq)
|
|
827
|
+
assert_nothing_raised do
|
|
828
|
+
assert Item.find(item.id)
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
should "should sleep more and more on each subsequent retry" do
|
|
833
|
+
item = create_item
|
|
834
|
+
seq = sequence("find")
|
|
835
|
+
Item.expects(:find_from_ids).with([item.id],{:auto_load => true}).raises(RightAws::ActiveSdb::ActiveSdbError, "Couldn't find Item with ID").in_sequence(seq)
|
|
836
|
+
Item.expects(:find_from_ids).with([item.id],{:auto_load => true}).raises(RightAws::ActiveSdb::ActiveSdbError, "Couldn't find Item with ID").in_sequence(seq)
|
|
837
|
+
Item.expects(:find_from_ids).with([item.id],{:auto_load => true}).raises(RightAws::ActiveSdb::ActiveSdbError, "Couldn't find Item with ID").in_sequence(seq)
|
|
838
|
+
|
|
839
|
+
sleepy = sequence("sleep")
|
|
840
|
+
Item.expects(:sleep).with(0.5).in_sequence(sleepy)
|
|
841
|
+
Item.expects(:sleep).with(1.0).in_sequence(sleepy)
|
|
842
|
+
assert_raise(SimplyStored::RecordNotFound) do
|
|
843
|
+
Item.find(item.id)
|
|
844
|
+
end
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
should "retry on system error" do
|
|
848
|
+
item = create_item
|
|
849
|
+
seq = sequence("find")
|
|
850
|
+
item.stubs(:sleep)
|
|
851
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(seq)
|
|
852
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(seq)
|
|
853
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).returns([item]).in_sequence(seq)
|
|
854
|
+
assert_nothing_raised do
|
|
855
|
+
assert Item.find(item.id)
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
should "eventually raise an error on system error" do
|
|
860
|
+
item = create_item
|
|
861
|
+
item.stubs(:sleep)
|
|
862
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).raises(Rightscale::AwsError, "RequestThrottled").times(3)
|
|
863
|
+
assert_raise(SimplyStored::Error) do
|
|
864
|
+
Item.find(item.id)
|
|
865
|
+
end
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
should "not retry on client error" do
|
|
869
|
+
item = create_item
|
|
870
|
+
Item.expects(:find_from_ids).with([item.id], {:auto_load => true}).raises(Rightscale::AwsError, "NoSuchDomain")
|
|
871
|
+
assert_raise(SimplyStored::Error) do
|
|
872
|
+
Item.find(item.id)
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
should "should auto load attributes by default" do
|
|
877
|
+
item = create_item
|
|
878
|
+
item = Item.find(item.id)
|
|
879
|
+
assert item.attributes.size > 1
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
context "when saving an instance" do
|
|
884
|
+
context "when saving fails due to errors" do
|
|
885
|
+
should "retry save when a system error was raised by simpledb" do
|
|
886
|
+
item = create_item
|
|
887
|
+
sleepy = sequence("sleep")
|
|
888
|
+
item.expects(:sleep).with(0.5).in_sequence(sleepy)
|
|
889
|
+
item.expects(:sleep).with(1.0).in_sequence(sleepy)
|
|
890
|
+
|
|
891
|
+
raiser = sequence("exception")
|
|
892
|
+
item.expects(:active_sdb_save).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
893
|
+
item.expects(:active_sdb_save).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
894
|
+
item.expects(:active_sdb_save).returns(true).in_sequence(raiser)
|
|
895
|
+
|
|
896
|
+
item.save
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
should "not retry when error was a client error" do
|
|
900
|
+
item = create_item
|
|
901
|
+
item.expects(:sleep).never
|
|
902
|
+
item.expects(:active_sdb_save).raises(Rightscale::AwsError, "Ron Telesky")
|
|
903
|
+
assert_raise(SimplyStored::Error) {item.save}
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
should "raise an error when the world has gone bad" do
|
|
907
|
+
item = create_item
|
|
908
|
+
item.expects(:active_sdb_save).times(3).raises(Rightscale::AwsError, "RequestThrottled")
|
|
909
|
+
assert_raise(SimplyStored::Error) {item.save}
|
|
910
|
+
end
|
|
911
|
+
end
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
context "when deleting an instance" do
|
|
915
|
+
should "actually delete the instance" do
|
|
916
|
+
@item.save
|
|
917
|
+
@item.delete
|
|
918
|
+
@item.reload
|
|
919
|
+
assert @item.attributes.empty?
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
should "retry on system error" do
|
|
923
|
+
item = create_item
|
|
924
|
+
sleepy = sequence("sleep")
|
|
925
|
+
item.expects(:sleep).with(0.5).in_sequence(sleepy)
|
|
926
|
+
item.expects(:sleep).with(1.0).in_sequence(sleepy)
|
|
927
|
+
|
|
928
|
+
raiser = sequence("exception")
|
|
929
|
+
item.expects(:active_sdb_delete).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
930
|
+
item.expects(:active_sdb_delete).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
931
|
+
item.expects(:active_sdb_delete).returns(true).in_sequence(raiser)
|
|
932
|
+
|
|
933
|
+
item.delete
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
should "not retry on client error" do
|
|
937
|
+
item = create_item
|
|
938
|
+
item.expects(:sleep).never
|
|
939
|
+
|
|
940
|
+
item.expects(:active_sdb_delete).raises(Rightscale::AwsError, "NoSuchDomain")
|
|
941
|
+
|
|
942
|
+
assert_raise(SimplyStored::Error) do
|
|
943
|
+
item.delete
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
should "eventually raise an error" do
|
|
948
|
+
item = create_item
|
|
949
|
+
item.stubs(:sleep)
|
|
950
|
+
|
|
951
|
+
item.expects(:active_sdb_delete).raises(Rightscale::AwsError, "NoSuchDomain")
|
|
952
|
+
|
|
953
|
+
assert_raise(SimplyStored::Error) do
|
|
954
|
+
item.delete
|
|
955
|
+
end
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
context "reload" do
|
|
961
|
+
should "return itself" do
|
|
962
|
+
item = create_item
|
|
963
|
+
assert item.reload.is_a?(Item)
|
|
964
|
+
assert_equal item, item.reload
|
|
965
|
+
end
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
context "ActiveRecord compatability" do
|
|
969
|
+
should "have a to_param helper" do
|
|
970
|
+
item = create_item
|
|
971
|
+
item.id = 9912
|
|
972
|
+
assert item.save
|
|
973
|
+
assert_equal "9912", item.id
|
|
974
|
+
assert_equal "9912", item.to_param
|
|
975
|
+
end
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
context "when updating attributes" do
|
|
979
|
+
setup do
|
|
980
|
+
@item = create_item
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
should "use save_attributes" do
|
|
984
|
+
@item.expects(:active_sdb_save_attributes).with('foo_attribute' => 'b')
|
|
985
|
+
|
|
986
|
+
@item.update_attributes('foo_attribute' => 'b')
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
should "nil a nil attribute" do
|
|
990
|
+
@item.update_attributes('foo_attribute' => 'hi')
|
|
991
|
+
assert_equal 'hi', @item.reload.foo_attribute
|
|
992
|
+
|
|
993
|
+
@item.update_attributes('foo_attribute' => nil)
|
|
994
|
+
assert_nil @item.reload.foo_attribute
|
|
995
|
+
end
|
|
996
|
+
|
|
997
|
+
should "nil an empty string attribute" do
|
|
998
|
+
@item.update_attributes('foo_attribute' => 'hi')
|
|
999
|
+
assert_equal 'hi', @item.reload.foo_attribute
|
|
1000
|
+
|
|
1001
|
+
@item.update_attributes('foo_attribute' => '')
|
|
1002
|
+
assert_nil @item.reload.foo_attribute
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1005
|
+
should "retry on system error" do
|
|
1006
|
+
sleepy = sequence("sleep")
|
|
1007
|
+
@item.expects(:sleep).with(0.5).in_sequence(sleepy)
|
|
1008
|
+
@item.expects(:sleep).with(1.0).in_sequence(sleepy)
|
|
1009
|
+
|
|
1010
|
+
raiser = sequence("exception")
|
|
1011
|
+
@item.expects(:active_sdb_save_attributes).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
1012
|
+
@item.expects(:active_sdb_save_attributes).raises(Rightscale::AwsError, "RequestThrottled").in_sequence(raiser)
|
|
1013
|
+
@item.expects(:active_sdb_save_attributes).returns(true).in_sequence(raiser)
|
|
1014
|
+
|
|
1015
|
+
@item.update_attributes(:foo_attribute => 'a')
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
should "not retry on client error" do
|
|
1019
|
+
@item.expects(:sleep).never
|
|
1020
|
+
|
|
1021
|
+
@item.expects(:active_sdb_save_attributes).raises(Rightscale::AwsError, "NoSuchDomain")
|
|
1022
|
+
|
|
1023
|
+
assert_raise(SimplyStored::Error) do
|
|
1024
|
+
@item.update_attributes(:foo_attribute => 'a')
|
|
1025
|
+
end
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
should "eventually raise an error on system error" do
|
|
1029
|
+
item = create_item
|
|
1030
|
+
item.stubs(:sleep)
|
|
1031
|
+
|
|
1032
|
+
item.expects(:active_sdb_save_attributes).raises(Rightscale::AwsError, "RequestThrottled").times(3)
|
|
1033
|
+
|
|
1034
|
+
assert_raise(SimplyStored::Error) do
|
|
1035
|
+
item.update_attributes(:foo_attribute => 'a')
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
context "namespaced classes" do
|
|
1042
|
+
|
|
1043
|
+
setup do
|
|
1044
|
+
ItemDaddy.instance_eval do
|
|
1045
|
+
belongs_to 'namespace__foo'
|
|
1046
|
+
end
|
|
1047
|
+
@foo = Namespace::Foo.new
|
|
1048
|
+
assert @foo.save
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
should "be able to adress them in belongs_to" do
|
|
1052
|
+
daddy = ItemDaddy.new(:name => 'abc')
|
|
1053
|
+
daddy.namespace__foo = @foo
|
|
1054
|
+
assert daddy.save
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
should "be able to call a has_many relation" do
|
|
1058
|
+
daddy = ItemDaddy.new(:name => 'abc')
|
|
1059
|
+
daddy.namespace__foo = @foo
|
|
1060
|
+
assert daddy.save
|
|
1061
|
+
|
|
1062
|
+
assert_equal daddy.id, ItemDaddy.find_by_namespace__foo_id(@foo.id).id
|
|
1063
|
+
assert_equal [daddy].map(&:id), @foo.item_daddys.map(&:id)
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
should "need special prefix inside namespace" do
|
|
1067
|
+
bar = Namespace::Bar.new
|
|
1068
|
+
assert bar.save
|
|
1069
|
+
@foo.namespace__bar = bar
|
|
1070
|
+
assert @foo.save
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
context "attributes longer than 1024 bytes" do
|
|
1075
|
+
setup do
|
|
1076
|
+
Item.instance_eval do
|
|
1077
|
+
simpledb_string :very_long
|
|
1078
|
+
end
|
|
1079
|
+
@item = create_item
|
|
1080
|
+
@item.very_long = '*' * 1600
|
|
1081
|
+
assert_equal 1600, @item.very_long.size
|
|
1082
|
+
end
|
|
1083
|
+
|
|
1084
|
+
should "add partitioned attributes to the class metadata"
|
|
1085
|
+
|
|
1086
|
+
should "not raise an error" do
|
|
1087
|
+
assert_nothing_raised do
|
|
1088
|
+
@item.save
|
|
1089
|
+
end
|
|
1090
|
+
end
|
|
1091
|
+
|
|
1092
|
+
should "split up the string into numbered attributes by 1024 bytes" do
|
|
1093
|
+
@item.save
|
|
1094
|
+
assert_equal 1024, @item["very_long_0"].first.size
|
|
1095
|
+
assert_equal 576, @item["very_long_1"].first.size
|
|
1096
|
+
end
|
|
1097
|
+
|
|
1098
|
+
should "store all characters and re-retrieve them on load" do
|
|
1099
|
+
@item.save
|
|
1100
|
+
@item.reload
|
|
1101
|
+
assert_equal 1600, @item.very_long.size
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
should "survive several reloads" do
|
|
1105
|
+
5.times do
|
|
1106
|
+
@item.save
|
|
1107
|
+
@item.reload
|
|
1108
|
+
end
|
|
1109
|
+
assert_equal 1600, @item.very_long.size
|
|
1110
|
+
assert_equal 1600, @item["very_long"].first.size
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
should "unset the original attribute after saving" do
|
|
1114
|
+
@item.save
|
|
1115
|
+
assert_equal nil, @item["very_long"]
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
should "still make the attribute available through its accessor" do
|
|
1119
|
+
@item.save
|
|
1120
|
+
assert_not_nil @item.very_long
|
|
1121
|
+
end
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
context "with attachments" do
|
|
1125
|
+
should "add a class method to specify an attachment" do
|
|
1126
|
+
assert SimplyStored::Simple.respond_to?(:has_s3_attachment)
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
should 'store the options in a class accessor' do
|
|
1130
|
+
assert LogItem.respond_to?(:_s3_options)
|
|
1131
|
+
assert_not_nil LogItem._s3_options
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
should "include an accessor for the _attachments" do
|
|
1135
|
+
assert LogItem.public_instance_methods.grep(/_s3_attachments/)
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
should "add accessors to the including class" do
|
|
1139
|
+
@log_item = LogItem.new
|
|
1140
|
+
assert @log_item.respond_to?(:log_data)
|
|
1141
|
+
assert @log_item.respond_to?(:log_data=)
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1144
|
+
should "return the assigned value with the accessor" do
|
|
1145
|
+
@log_item = LogItem.new
|
|
1146
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1147
|
+
assert_equal "Yay! It logged!", @log_item.log_data
|
|
1148
|
+
end
|
|
1149
|
+
|
|
1150
|
+
should "mark the attachment as changed when assigned a new value" do
|
|
1151
|
+
@log_item = LogItem.new
|
|
1152
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1153
|
+
assert @log_item._s3_attachments[:log_data][:dirty]
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
should "not include the assigned value to the simpledb attributes" do
|
|
1157
|
+
@log_item = LogItem.new
|
|
1158
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1159
|
+
assert_nil @log_item.attributes[:log_data]
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
should "raise an error when no bucket was specified" do
|
|
1163
|
+
assert_raise(ArgumentError, "No bucket name specified") do
|
|
1164
|
+
LogItem.class_eval do
|
|
1165
|
+
has_s3_attachment :bla
|
|
1166
|
+
end
|
|
1167
|
+
end
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
context "with s3 interaction" do
|
|
1171
|
+
setup do
|
|
1172
|
+
LogItem.instance_variable_set(:@_s3_connection, nil)
|
|
1173
|
+
|
|
1174
|
+
bucket = stub(:bckt) do
|
|
1175
|
+
stubs(:put).returns(true)
|
|
1176
|
+
stubs(:get).returns(true)
|
|
1177
|
+
end
|
|
1178
|
+
|
|
1179
|
+
@bucket = bucket
|
|
1180
|
+
|
|
1181
|
+
@s3 = stub(:s3) do
|
|
1182
|
+
stubs(:bucket).returns(bucket)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
RightAws::S3.stubs(:new).returns @s3
|
|
1186
|
+
@log_item = LogItem.new
|
|
1187
|
+
end
|
|
1188
|
+
|
|
1189
|
+
context "when saving the attachment" do
|
|
1190
|
+
should "fetch the collection" do
|
|
1191
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1192
|
+
RightAws::S3.expects(:new).with('abcdef', 'secret!', :multi_thread => true).returns(@s3)
|
|
1193
|
+
@log_item.save
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
should "upload the file" do
|
|
1197
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1198
|
+
@bucket.expects(:put).with(anything, "Yay! It logged!", {}, anything)
|
|
1199
|
+
@log_item.save
|
|
1200
|
+
end
|
|
1201
|
+
|
|
1202
|
+
should "use the specified bucket" do
|
|
1203
|
+
@log_item.log_data = "Yay! It logged!"
|
|
1204
|
+
LogItem._s3_options[:log_data][:bucket] = 'mybucket'
|
|
1205
|
+
@s3.expects(:bucket).with('mybucket').returns(@bucket)
|
|
1206
|
+
@log_item.save
|
|
1207
|
+
end
|
|
1208
|
+
|
|
1209
|
+
should "create the bucket if it doesn't exist" do
|
|
1210
|
+
@log_item.log_data = "Yay! log me"
|
|
1211
|
+
LogItem._s3_options[:log_data][:bucket] = 'mybucket'
|
|
1212
|
+
|
|
1213
|
+
@s3.expects(:bucket).with('mybucket').returns(nil)
|
|
1214
|
+
@s3.expects(:bucket).with('mybucket', true, 'private').returns(@bucket)
|
|
1215
|
+
@log_item.save
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
should "raise an error if the bucket is not ours" do
|
|
1219
|
+
@log_item.log_data = "Yay! log me too"
|
|
1220
|
+
LogItem._s3_options[:log_data][:bucket] = 'mybucket'
|
|
1221
|
+
|
|
1222
|
+
@s3.expects(:bucket).with('mybucket').returns(nil)
|
|
1223
|
+
@s3.expects(:bucket).with('mybucket', true, 'private').raises(RightAws::AwsError, 'BucketAlreadyExists: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again')
|
|
1224
|
+
|
|
1225
|
+
assert_raise(ArgumentError) do
|
|
1226
|
+
@log_item.save
|
|
1227
|
+
end
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
should "not upload the attachment when it hasn't been changed" do
|
|
1231
|
+
@bucket.expects(:put).never
|
|
1232
|
+
@log_item.save
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
should "set the permissions to private by default" do
|
|
1236
|
+
class PrivateLogItem < SimplyStored::Simple
|
|
1237
|
+
has_s3_attachment :log_data, :bucket => 'mybucket'
|
|
1238
|
+
end
|
|
1239
|
+
PrivateLogItem.create_domain
|
|
1240
|
+
|
|
1241
|
+
@bucket.expects(:put).with(anything, anything, {}, 'private')
|
|
1242
|
+
@log_item = PrivateLogItem.new
|
|
1243
|
+
|
|
1244
|
+
@log_item.log_data = 'Yay!'
|
|
1245
|
+
@log_item.save
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1248
|
+
should "set the permissions to whatever's specified in the options for the attachment" do
|
|
1249
|
+
@log_item.save
|
|
1250
|
+
old_perms = LogItem._s3_options[:log_data][:permissions]
|
|
1251
|
+
LogItem._s3_options[:log_data][:permissions] = 'public-read'
|
|
1252
|
+
@bucket.expects(:put).with(anything, anything, {}, 'public-read')
|
|
1253
|
+
@log_item.log_data = 'Yay!'
|
|
1254
|
+
@log_item.save
|
|
1255
|
+
LogItem._s3_options[:log_data][:permissions] = old_perms
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
should "use the full class name and the id as key" do
|
|
1259
|
+
@log_item.save
|
|
1260
|
+
@bucket.expects(:put).with("log_items/log_data/#{@log_item.id}", 'Yay!', {}, anything)
|
|
1261
|
+
@log_item.log_data = 'Yay!'
|
|
1262
|
+
@log_item.save
|
|
1263
|
+
end
|
|
1264
|
+
|
|
1265
|
+
should "mark the attachment as not dirty after uploading" do
|
|
1266
|
+
@log_item.log_data = 'Yay!'
|
|
1267
|
+
@log_item.save
|
|
1268
|
+
assert !@log_item.instance_variable_get(:@_s3_attachments)[:log_data][:dirty]
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
should 'store the attachment when the validations succeeded' do
|
|
1272
|
+
@log_item.log_data = 'Yay!'
|
|
1273
|
+
@log_item.stubs(:valid?).returns(true)
|
|
1274
|
+
@bucket.expects(:put)
|
|
1275
|
+
@log_item.save
|
|
1276
|
+
end
|
|
1277
|
+
|
|
1278
|
+
should "not store the attachment when the validations failed" do
|
|
1279
|
+
@log_item.log_data = 'Yay!'
|
|
1280
|
+
@log_item.stubs(:valid?).returns(false)
|
|
1281
|
+
@bucket.expects(:put).never
|
|
1282
|
+
@log_item.save
|
|
1283
|
+
end
|
|
1284
|
+
|
|
1285
|
+
should "save the attachment status" do
|
|
1286
|
+
@log_item.save
|
|
1287
|
+
@log_item.attributes["log_data_attachments"]
|
|
1288
|
+
end
|
|
1289
|
+
|
|
1290
|
+
should "save generate the url for the attachment" do
|
|
1291
|
+
@log_item._s3_options[:log_data][:bucket] = 'bucket-for-monsieur'
|
|
1292
|
+
@log_item._s3_options[:log_data][:permissions] = 'public-read'
|
|
1293
|
+
@log_item.save
|
|
1294
|
+
assert_equal "http://bucket-for-monsieur.s3.amazonaws.com/#{@log_item.s3_attachment_key(:log_data)}", @log_item.log_data_url
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
should "add a short-lived access key for private attachments" do
|
|
1298
|
+
@log_item._s3_options[:log_data][:permissions] = 'private'
|
|
1299
|
+
@log_item.save
|
|
1300
|
+
assert @log_item.log_data_url.include?("https://bucket-for-monsieur.s3.amazonaws.com:443/#{@log_item.s3_attachment_key(:log_data)}")
|
|
1301
|
+
assert @log_item.log_data_url.include?("Signature=")
|
|
1302
|
+
assert @log_item.log_data_url.include?("Expires=")
|
|
1303
|
+
end
|
|
1304
|
+
|
|
1305
|
+
should "serialize data other than strings to json" do
|
|
1306
|
+
@log_item.log_data = ['one log entry', 'and another one']
|
|
1307
|
+
@bucket.expects(:put).with(anything, '["one log entry","and another one"]', {}, anything)
|
|
1308
|
+
@log_item.save
|
|
1309
|
+
end
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
context "when fetching the data" do
|
|
1313
|
+
should "fetch the data from s3 and set the attachment attribute" do
|
|
1314
|
+
@log_item.instance_variable_set(:@_s3_attachments, {})
|
|
1315
|
+
@bucket.expects(:get).with("log_items/log_data/#{@log_item.id}").returns("Yay!")
|
|
1316
|
+
assert_equal "Yay!", @log_item.log_data
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
should "not mark the the attachment as dirty" do
|
|
1320
|
+
@log_item.instance_variable_set(:@_s3_attachments, {})
|
|
1321
|
+
@bucket.expects(:get).with("log_items/log_data/#{@log_item.id}").returns("Yay!")
|
|
1322
|
+
@log_item.log_data
|
|
1323
|
+
assert !@log_item._s3_attachments[:log_data][:dirty]
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
should "not try to fetch the attachment if the value is already set" do
|
|
1327
|
+
@log_item.log_data = "Yay!"
|
|
1328
|
+
@bucket.expects(:get).never
|
|
1329
|
+
assert_equal "Yay!", @log_item.log_data
|
|
1330
|
+
end
|
|
1331
|
+
end
|
|
1332
|
+
end
|
|
1333
|
+
end
|
|
1334
|
+
end
|
|
1335
|
+
|
|
1336
|
+
def create_item
|
|
1337
|
+
i = Item.new(:foo_attribute => 'a', :bar_attribute_array => ['goog'])
|
|
1338
|
+
assert i.save
|
|
1339
|
+
i
|
|
1340
|
+
end
|
|
1341
|
+
end
|