simply_stored 0.3.9 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +38 -0
- data/README.md +45 -24
- data/lib/simply_stored/couch/association_property.rb +27 -0
- data/lib/simply_stored/couch/belongs_to.rb +5 -7
- data/lib/simply_stored/couch/finders.rb +5 -1
- data/lib/simply_stored/couch/has_and_belongs_to_many.rb +202 -0
- data/lib/simply_stored/couch/has_many.rb +6 -51
- data/lib/simply_stored/couch/has_one.rb +4 -29
- data/lib/simply_stored/couch/properties.rb +11 -0
- data/lib/simply_stored/couch.rb +38 -2
- data/lib/simply_stored/instance_methods.rb +68 -29
- data/lib/simply_stored.rb +3 -1
- data/test/{couchdb/couch_active_model_compatibility_test.rb → active_model_compatibility_test.rb} +2 -2
- data/test/{couchdb/couch_belongs_to_test.rb → belongs_to_test.rb} +13 -3
- data/test/{couchdb/couch_conflict_handling_test.rb → conflict_handling_test.rb} +3 -3
- data/test/{couchdb/couch_finder_test.rb → finder_test.rb} +8 -3
- data/test/fixtures/couch.rb +55 -0
- data/test/has_and_belongs_to_many_test.rb +639 -0
- data/test/{couchdb/couch_has_many_test.rb → has_many_test.rb} +13 -3
- data/test/{couchdb/couch_has_one_test.rb → has_one_test.rb} +13 -3
- data/test/{couchdb/couch_instance_lifecycle_test.rb → instance_lifecycle_test.rb} +3 -3
- data/test/{couchdb/couch_mass_assignment_protection_test.rb → mass_assignment_protection_test.rb} +3 -3
- data/test/{couchdb/couch_s3_test.rb → s3_test.rb} +3 -3
- data/test/{couchdb/couch_soft_deletable_test.rb → soft_deletable_test.rb} +3 -3
- data/test/test_helper.rb +1 -5
- data/test/{couchdb/couch_validations_test.rb → validations_test.rb} +3 -3
- data/test/{couchdb/custom_views_test.rb → views_test.rb} +3 -3
- metadata +36 -234
- data/lib/simply_stored/simpledb/associations.rb +0 -215
- data/lib/simply_stored/simpledb/attributes.rb +0 -173
- data/lib/simply_stored/simpledb/storag.rb +0 -85
- data/lib/simply_stored/simpledb/validations.rb +0 -88
- data/lib/simply_stored/simpledb.rb +0 -216
- data/test/fixtures/simpledb/item.rb +0 -11
- data/test/fixtures/simpledb/item_daddy.rb +0 -8
- data/test/fixtures/simpledb/log_item.rb +0 -3
- data/test/fixtures/simpledb/namespace_bar.rb +0 -5
- data/test/fixtures/simpledb/namespace_foo.rb +0 -7
- data/test/fixtures/simpledb/protected_item.rb +0 -3
- data/test/simply_stored_simpledb_test.rb +0 -1376
- data/test/vendor/dhaka-2.2.1/lib/dhaka/dot/dot.rb +0 -29
- data/test/vendor/dhaka-2.2.1/lib/dhaka/evaluator/evaluator.rb +0 -133
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/closure_hash.rb +0 -15
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar.rb +0 -240
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar_symbol.rb +0 -27
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/precedence.rb +0 -19
- data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/production.rb +0 -36
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/accept_actions.rb +0 -36
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/alphabet.rb +0 -21
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/compiled_lexer.rb +0 -46
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/dfa.rb +0 -121
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexeme.rb +0 -32
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer.rb +0 -70
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer_run.rb +0 -78
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_grammar.rb +0 -392
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_parser.rb +0 -2010
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_tokenizer.rb +0 -14
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/specification.rb +0 -96
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state.rb +0 -68
- data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state_machine.rb +0 -37
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/action.rb +0 -55
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/channel.rb +0 -58
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/compiled_parser.rb +0 -51
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/conflict.rb +0 -54
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/item.rb +0 -42
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_result.rb +0 -50
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_tree.rb +0 -66
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser.rb +0 -165
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_methods.rb +0 -11
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_run.rb +0 -39
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_state.rb +0 -74
- data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/token.rb +0 -22
- data/test/vendor/dhaka-2.2.1/lib/dhaka/runtime.rb +0 -51
- data/test/vendor/dhaka-2.2.1/lib/dhaka/tokenizer/tokenizer.rb +0 -190
- data/test/vendor/dhaka-2.2.1/lib/dhaka.rb +0 -62
- data/test/vendor/dhaka-2.2.1/test/all_tests.rb +0 -5
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator.rb +0 -64
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator_test.rb +0 -43
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar.rb +0 -41
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar_test.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_test_methods.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer.rb +0 -39
- data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer_test.rb +0 -38
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_evaluator.rb +0 -43
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +0 -24
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar_test.rb +0 -30
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_lexer_specification.rb +0 -23
- data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +0 -33
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_grammar.rb +0 -23
- data/test/vendor/dhaka-2.2.1/test/brackets/bracket_tokenizer.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/brackets/brackets_test.rb +0 -28
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver.rb +0 -46
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver_test.rb +0 -276
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator.rb +0 -284
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator_test.rb +0 -38
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_grammar.rb +0 -104
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer.rb +0 -109
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_specification.rb +0 -37
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_test.rb +0 -58
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser.rb +0 -879
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser_test.rb +0 -55
- data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_test.rb +0 -170
- data/test/vendor/dhaka-2.2.1/test/core/another_lalr_but_not_slr_grammar.rb +0 -20
- data/test/vendor/dhaka-2.2.1/test/core/compiled_parser_test.rb +0 -44
- data/test/vendor/dhaka-2.2.1/test/core/dfa_test.rb +0 -170
- data/test/vendor/dhaka-2.2.1/test/core/evaluator_test.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/core/grammar_test.rb +0 -83
- data/test/vendor/dhaka-2.2.1/test/core/lalr_but_not_slr_grammar.rb +0 -19
- data/test/vendor/dhaka-2.2.1/test/core/lexer_test.rb +0 -139
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar.rb +0 -7
- data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar_test.rb +0 -8
- data/test/vendor/dhaka-2.2.1/test/core/nullable_grammar.rb +0 -21
- data/test/vendor/dhaka-2.2.1/test/core/parse_result_test.rb +0 -44
- data/test/vendor/dhaka-2.2.1/test/core/parser_state_test.rb +0 -24
- data/test/vendor/dhaka-2.2.1/test/core/parser_test.rb +0 -131
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar.rb +0 -17
- data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar_test.rb +0 -9
- data/test/vendor/dhaka-2.2.1/test/core/rr_conflict_grammar.rb +0 -21
- data/test/vendor/dhaka-2.2.1/test/core/simple_grammar.rb +0 -22
- data/test/vendor/dhaka-2.2.1/test/core/sr_conflict_grammar.rb +0 -16
- data/test/vendor/dhaka-2.2.1/test/dhaka_test_helper.rb +0 -17
- data/test/vendor/dhaka-2.2.1/test/fake_logger.rb +0 -17
- data/test/vendor/simplerdb-0.2/lib/simplerdb/client_exception.rb +0 -10
- data/test/vendor/simplerdb-0.2/lib/simplerdb/db.rb +0 -146
- data/test/vendor/simplerdb-0.2/lib/simplerdb/query_language.rb +0 -266
- data/test/vendor/simplerdb-0.2/lib/simplerdb/server.rb +0 -33
- data/test/vendor/simplerdb-0.2/lib/simplerdb/servlet.rb +0 -191
- data/test/vendor/simplerdb-0.2/lib/simplerdb.rb +0 -3
- data/test/vendor/simplerdb-0.2/test/functional_test.rb +0 -81
- data/test/vendor/simplerdb-0.2/test/query_evaluator_test.rb +0 -73
- data/test/vendor/simplerdb-0.2/test/query_parser_test.rb +0 -64
- data/test/vendor/simplerdb-0.2/test/simplerdb_test.rb +0 -80
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,44 @@
|
|
1
1
|
Changelog
|
2
2
|
=============
|
3
3
|
|
4
|
+
0.5.0
|
5
|
+
|
6
|
+
- Add support for has_and_belongs_to_many relations:
|
7
|
+
|
8
|
+
n:m relations where the IDs are stored on one part as an array:
|
9
|
+
|
10
|
+
class Server
|
11
|
+
include SimplyStored::Couch
|
12
|
+
|
13
|
+
property :hostname
|
14
|
+
|
15
|
+
has_and_belongs_to_many :networks, :storing_keys => true
|
16
|
+
end
|
17
|
+
|
18
|
+
class Network
|
19
|
+
include SimplyStored::Couch
|
20
|
+
|
21
|
+
property :klass
|
22
|
+
|
23
|
+
has_and_belongs_to_many :servers, :storing_keys => false
|
24
|
+
end
|
25
|
+
|
26
|
+
network = Network.create(:klass => "A")
|
27
|
+
server = Server.new(:hostname => 'www.example.com')
|
28
|
+
network.add_server(server)
|
29
|
+
server.network_ids # => [network.id]
|
30
|
+
network.servers # => [server]
|
31
|
+
server.networks # => [network]
|
32
|
+
|
33
|
+
The array property holding the IDs of the other item will be used to constuct two view to lookup
|
34
|
+
the other part. Soft deleting is only supported on the class holding the IDs.
|
35
|
+
|
36
|
+
- Add support for .last - which is the same as first by reverse order
|
37
|
+
|
38
|
+
User.last # => User.find(:first, :order => :desc)
|
39
|
+
|
40
|
+
- Drop support for SimpleDB
|
41
|
+
|
4
42
|
0.3.8
|
5
43
|
|
6
44
|
- Fix loading of has_many/has_one associations for inherited relations.
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
Convenience layer for CouchDB
|
1
|
+
Convenience layer for CouchDB on top of CouchPotato.
|
2
2
|
|
3
|
-
SimplyStored allows you to persist your objects to CouchDB
|
3
|
+
SimplyStored allows you to persist your objects to CouchDB using an ActiveRecord-like syntax.
|
4
4
|
|
5
5
|
In contrast to [CouchPotato](http://github.com/langalex/couch_potato) (on top of it is build)
|
6
6
|
it supports associations and other syntactic sugar that makes ActiveRecord so appealing.
|
7
7
|
|
8
|
-
|
8
|
+
SimplyStored has also support for S3 attachments.
|
9
9
|
|
10
10
|
See also [RockingChair](http://github.com/jweiss/rocking_chair) on how to speed-up your unit tests
|
11
11
|
by using an in-memory CouchDB backend.
|
@@ -20,25 +20,17 @@ Installation
|
|
20
20
|
Usage
|
21
21
|
=============
|
22
22
|
|
23
|
-
Require
|
23
|
+
Require SimplyStored:
|
24
24
|
|
25
|
-
require 'simply_stored
|
25
|
+
require 'simply_stored'
|
26
26
|
CouchPotato::Config.database_name = "http://example.com:5984/name_of_the_db"
|
27
|
-
|
28
|
-
or
|
29
|
-
|
30
|
-
require 'simply_stored/simpledb'
|
31
|
-
SimplyStored::Simple.aws_access_key = 'foo'
|
32
|
-
SimplyStored::Simple.aws_secret_access_key = 'bar'
|
33
|
-
RightAws::ActiveSdb.establish_connection(SimplyStored::Simple.aws_access_key, SimplyStored::Simple.aws_secret_access_key, :protocol => 'https')
|
34
|
-
|
27
|
+
|
35
28
|
From now on you can define classes that use SimplyStored.
|
36
29
|
|
37
|
-
|
30
|
+
Intro
|
38
31
|
=============
|
39
32
|
|
40
|
-
|
41
|
-
all the serialization and de-serialization stuff.
|
33
|
+
SimplyStored auto-generates views for you and handles all the serialization and de-serialization stuff.
|
42
34
|
|
43
35
|
class User
|
44
36
|
include SimplyStored::Couch
|
@@ -85,10 +77,10 @@ all the serialization and de-serialization stuff.
|
|
85
77
|
# => []
|
86
78
|
|
87
79
|
|
88
|
-
|
80
|
+
Associations
|
89
81
|
=============
|
90
82
|
|
91
|
-
The supported associations are: belongs_to, has_one, has_many,
|
83
|
+
The supported associations are: belongs_to, has_one, has_many, has_many :through, and has_and_belongs_to_many:
|
92
84
|
|
93
85
|
class Post
|
94
86
|
include SimplyStored::Couch
|
@@ -136,8 +128,36 @@ The supported associations are: belongs_to, has_one, has_many, and has_many :thr
|
|
136
128
|
post.user_count
|
137
129
|
# => 2
|
138
130
|
|
131
|
+
n:m relations where the IDs are stored on one part as an array:
|
132
|
+
|
133
|
+
class Server
|
134
|
+
include SimplyStored::Couch
|
135
|
+
|
136
|
+
property :hostname
|
137
|
+
|
138
|
+
has_and_belongs_to_many :networks, :storing_keys => true
|
139
|
+
end
|
140
|
+
|
141
|
+
class Network
|
142
|
+
include SimplyStored::Couch
|
143
|
+
|
144
|
+
property :klass
|
145
|
+
|
146
|
+
has_and_belongs_to_many :servers, :storing_keys => false
|
147
|
+
end
|
148
|
+
|
149
|
+
network = Network.create(:klass => "A")
|
150
|
+
server = Server.new(:hostname => 'www.example.com')
|
151
|
+
network.add_server(server)
|
152
|
+
server.network_ids # => [network.id]
|
153
|
+
network.servers # => [server]
|
154
|
+
server.networks # => [network]
|
139
155
|
|
140
|
-
|
156
|
+
The array property holding the IDs of the other item will be used to constuct two view to lookup
|
157
|
+
the other part. Soft deleting is only supported on the class holding the IDs.
|
158
|
+
|
159
|
+
Custom Associations
|
160
|
+
=============
|
141
161
|
|
142
162
|
class Document
|
143
163
|
include SimplyStored::Couch
|
@@ -150,7 +170,7 @@ CouchDB - Custom Associations
|
|
150
170
|
d.creator = User.first
|
151
171
|
|
152
172
|
|
153
|
-
|
173
|
+
Validations
|
154
174
|
=============
|
155
175
|
|
156
176
|
Further, you can have validations (using the validatable gem)
|
@@ -181,10 +201,11 @@ Further, you can have validations (using the validatable gem)
|
|
181
201
|
# => raises CouchPotato::Database::ValidationsFailedError: #<CouchPotato::Database::ValidationsFailedError:0x102571130>
|
182
202
|
|
183
203
|
|
184
|
-
|
204
|
+
S3 Attachments
|
185
205
|
=============
|
186
206
|
|
187
|
-
|
207
|
+
SimplyStored supports storing large attachments in Amazon S3.
|
208
|
+
It uses RightAWS for the interaction with the EC2 API:
|
188
209
|
|
189
210
|
class Log
|
190
211
|
include SimplyStored::Couch
|
@@ -205,10 +226,10 @@ Both the CouchDB backend and the SimpleDB backend have support for S3 attachment
|
|
205
226
|
log.data_size
|
206
227
|
# => 11238132
|
207
228
|
|
208
|
-
This will create an item on S3 in the specified bucket. The item will use the ID of the log object as the key and the body will be the data attribute. This way you can store big files outside of CouchDB
|
229
|
+
This will create an item on S3 in the specified bucket. The item will use the ID of the log object as the key and the body will be the data attribute. This way you can store big files outside of CouchDB.
|
209
230
|
|
210
231
|
|
211
|
-
|
232
|
+
Soft delete
|
212
233
|
=============
|
213
234
|
|
214
235
|
SimplyStored also has support for "soft deleting" - much like acts_as_paranoid. Items will then not be deleted but only marked as deleted. This way you can recover them later.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SimplyStored
|
2
|
+
module Couch
|
3
|
+
class AssociationProperty
|
4
|
+
attr_reader :name, :options
|
5
|
+
|
6
|
+
def dirty?(object)
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
def build(object, json)
|
11
|
+
end
|
12
|
+
|
13
|
+
def serialize(json, object)
|
14
|
+
end
|
15
|
+
alias :value :serialize
|
16
|
+
|
17
|
+
def supports_dirty?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def association?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
module SimplyStored
|
3
3
|
module Couch
|
4
4
|
module BelongsTo
|
5
|
-
|
5
|
+
include SimplyStored::Couch::Properties
|
6
|
+
|
6
7
|
def belongs_to(name, options = {})
|
8
|
+
check_existing_properties(name, SimplyStored::Couch::BelongsTo::Property)
|
9
|
+
|
7
10
|
map_definition_without_deleted = <<-eos
|
8
11
|
function(doc) {
|
9
12
|
if (doc['ruby_class'] == '#{self.to_s}' && doc['#{name.to_s}_id'] != null) {
|
@@ -17,11 +20,6 @@ module SimplyStored
|
|
17
20
|
eos
|
18
21
|
|
19
22
|
reduce_definition = "_sum"
|
20
|
-
# reduce_definition = <<-eos
|
21
|
-
# function(key, values) {
|
22
|
-
# return sum(values);
|
23
|
-
# }
|
24
|
-
# eos
|
25
23
|
|
26
24
|
view "association_#{self.name.underscore}_belongs_to_#{name}",
|
27
25
|
:map => map_definition_without_deleted,
|
@@ -119,4 +117,4 @@ module SimplyStored
|
|
119
117
|
end
|
120
118
|
end
|
121
119
|
end
|
122
|
-
end
|
120
|
+
end
|
@@ -40,7 +40,11 @@ module SimplyStored
|
|
40
40
|
def first(*args)
|
41
41
|
find(:first, *args)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
|
+
def last(*args)
|
45
|
+
find(:first, :order => :desc, *args)
|
46
|
+
end
|
47
|
+
|
44
48
|
def count(options = {})
|
45
49
|
options.assert_valid_keys(:with_deleted)
|
46
50
|
with_deleted = options[:with_deleted]
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module SimplyStored
|
2
|
+
module Couch
|
3
|
+
module HasAndBelongsToMany
|
4
|
+
def has_and_belongs_to_many(name, options = {})
|
5
|
+
check_existing_properties(name, SimplyStored::Couch::HasAndBelongsToMany::Property)
|
6
|
+
properties << SimplyStored::Couch::HasAndBelongsToMany::Property.new(self, name, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def define_has_and_belongs_to_many_property(foreign_key)
|
10
|
+
property foreign_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def define_has_and_belongs_to_many_views(name, options)
|
14
|
+
key_order = options[:class_storing_keys] == self.name ? "doc.#{options[:foreign_key]}[index], doc._id" : "doc._id, doc.#{options[:foreign_key]}[index]"
|
15
|
+
value = options[:class_storing_keys] == self.name ? 1 : "{ _id :doc.#{options[:foreign_key]}[index]}"
|
16
|
+
|
17
|
+
map_definition_without_deleted = <<-eos
|
18
|
+
function(doc) {
|
19
|
+
if (doc['ruby_class'] == '#{options[:class_storing_keys]}' && doc['#{options[:foreign_key]}'] != null) {
|
20
|
+
if (doc['#{soft_delete_attribute}'] && doc['#{soft_delete_attribute}'] != null){
|
21
|
+
// "soft" deleted
|
22
|
+
}else{
|
23
|
+
for (var index in doc.#{options[:foreign_key]}) {
|
24
|
+
emit([#{key_order}], #{value});
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
eos
|
30
|
+
|
31
|
+
reduce_definition = options[:class_storing_keys] == self.name ? "_sum" : <<-eos
|
32
|
+
function(key, values) {
|
33
|
+
var sum = 0;
|
34
|
+
for (var i in values){
|
35
|
+
if (typeof(i) == 'number'){
|
36
|
+
sum = sum + i;
|
37
|
+
} else {
|
38
|
+
sum = sum + 1;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
return sum;
|
42
|
+
}
|
43
|
+
eos
|
44
|
+
|
45
|
+
view "association_#{self.name.underscore}_has_and_belongs_to_many_#{name}",
|
46
|
+
:map => map_definition_without_deleted,
|
47
|
+
:reduce => reduce_definition,
|
48
|
+
:type => "custom",
|
49
|
+
:include_docs => true
|
50
|
+
|
51
|
+
map_definition_with_deleted = <<-eos
|
52
|
+
function(doc) {
|
53
|
+
if (doc['ruby_class'] == '#{options[:class_storing_keys]}' && doc['#{options[:foreign_key]}'] != null) {
|
54
|
+
for (var index in doc.#{options[:foreign_key]}) {
|
55
|
+
emit([#{key_order}], #{value});
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
eos
|
60
|
+
|
61
|
+
view "association_#{self.name.underscore}_has_and_belongs_to_many_#{name}_with_deleted",
|
62
|
+
:map => map_definition_with_deleted,
|
63
|
+
:reduce => reduce_definition,
|
64
|
+
:type => "custom",
|
65
|
+
:include_docs => true
|
66
|
+
end
|
67
|
+
|
68
|
+
def define_has_and_belongs_to_many_getter(name, options)
|
69
|
+
define_method(name) do |*args|
|
70
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
71
|
+
forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
|
72
|
+
|
73
|
+
cached_results = send("_get_cached_#{name}")
|
74
|
+
cache_key = _cache_key_for(local_options)
|
75
|
+
if forced_reload || cached_results[cache_key].nil?
|
76
|
+
cached_results[cache_key] = find_associated_via_join_view(options[:class_name], self.class, :with_deleted => with_deleted, :limit => limit, :descending => descending, :foreign_key => options[:foreign_key])
|
77
|
+
instance_variable_set("@#{name}", cached_results)
|
78
|
+
end
|
79
|
+
cached_results[cache_key]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def define_has_and_belongs_to_many_setter_add(name, options)
|
84
|
+
define_method("add_#{name.to_s.singularize}") do |value|
|
85
|
+
klass = self.class.get_class_from_name(name)
|
86
|
+
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
|
87
|
+
|
88
|
+
if options[:class_storing_keys] == self.class.name
|
89
|
+
self.send("#{options[:foreign_key]}=", ((send(options[:foreign_key]) || []) + [value.id]).uniq )
|
90
|
+
self.save(false)
|
91
|
+
else
|
92
|
+
value.send("#{options[:foreign_key]}=", ((value.send(options[:foreign_key]) || []) + [self.id]).uniq )
|
93
|
+
value.save(false)
|
94
|
+
end
|
95
|
+
|
96
|
+
cached_results = send("_get_cached_#{name}")[:all]
|
97
|
+
send("_set_cached_#{name}", (cached_results || []) << value, :all)
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def define_has_and_belongs_to_many_setter_remove(name, options)
|
103
|
+
define_method "remove_#{name.to_s.singularize}" do |value|
|
104
|
+
klass = self.class.get_class_from_name(name)
|
105
|
+
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
|
106
|
+
|
107
|
+
if options[:class_storing_keys] == self.class.name
|
108
|
+
raise ArgumentError, "cannot remove not mine" unless (send(options[:foreign_key]) || []).include?(value.id)
|
109
|
+
else
|
110
|
+
raise ArgumentError, "cannot remove not mine" unless (value.send(options[:foreign_key]) || []).include?(id)
|
111
|
+
end
|
112
|
+
|
113
|
+
if options[:class_storing_keys] == self.class.name
|
114
|
+
foreign_keys = (send(options[:foreign_key]) || []) - [value.id]
|
115
|
+
send("#{options[:foreign_key]}=", foreign_keys)
|
116
|
+
save(false)
|
117
|
+
else
|
118
|
+
foreign_keys = (value.send(options[:foreign_key]) || []) - [self.id]
|
119
|
+
value.send("#{options[:foreign_key]}=", foreign_keys)
|
120
|
+
value.save(false)
|
121
|
+
end
|
122
|
+
|
123
|
+
cached_results = send("_get_cached_#{name}")[:all]
|
124
|
+
send("_set_cached_#{name}", (cached_results || []).delete_if{|item| item.id == value.id}, :all)
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def define_has_and_belongs_to_many_setter_remove_all(name, options)
|
130
|
+
define_method "remove_all_#{name}" do
|
131
|
+
all = send("#{name}", :force_reload => true)
|
132
|
+
|
133
|
+
all.collect{|i| i}.each do |item|
|
134
|
+
send("remove_#{name.to_s.singularize}", item)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def define_has_and_belongs_to_many_count(name, options, through = nil)
|
140
|
+
method_name = name.to_s.singularize.underscore + "_count"
|
141
|
+
define_method(method_name) do |*args|
|
142
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
143
|
+
forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
|
144
|
+
|
145
|
+
if forced_reload || instance_variable_get("@#{method_name}").nil?
|
146
|
+
instance_variable_set("@#{method_name}", count_associated_via_join_view(through || options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key]))
|
147
|
+
end
|
148
|
+
instance_variable_get("@#{method_name}")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def define_has_and_belongs_to_many_after_destroy_cleanup(name, options)
|
153
|
+
if options[:class_storing_keys] == self.name
|
154
|
+
define_method "has_and_belongs_to_many_clean_up_after_destroy" do |property|
|
155
|
+
nil # deleting is enough as we store the keys
|
156
|
+
end
|
157
|
+
else
|
158
|
+
define_method "has_and_belongs_to_many_clean_up_after_destroy" do |property|
|
159
|
+
send("remove_all_#{property.name}")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Property < SimplyStored::Couch::AssociationProperty
|
165
|
+
|
166
|
+
def initialize(owner_clazz, name, options = {})
|
167
|
+
options = {
|
168
|
+
:storing_keys => false,
|
169
|
+
:class_name => name.to_s.singularize.camelize,
|
170
|
+
:foreign_key => nil,
|
171
|
+
}.update(options)
|
172
|
+
|
173
|
+
# there is only one pair of foreign_keys and it usualy the name of the class not storing the keys
|
174
|
+
if options[:foreign_key].blank?
|
175
|
+
if options[:storing_keys]
|
176
|
+
options[:foreign_key] = options[:class_name].singularize.underscore.foreign_key.pluralize
|
177
|
+
else
|
178
|
+
options[:foreign_key] = owner_clazz.name.singularize.underscore.foreign_key.pluralize
|
179
|
+
end
|
180
|
+
end
|
181
|
+
options[:class_storing_keys] = options[:storing_keys] ? owner_clazz.name : options[:class_name]
|
182
|
+
@name, @options = name, options
|
183
|
+
|
184
|
+
options.assert_valid_keys(:class_name, :foreign_key, :storing_keys, :class_storing_keys)
|
185
|
+
|
186
|
+
owner_clazz.class_eval do
|
187
|
+
_define_cache_accessors(name, options)
|
188
|
+
define_has_and_belongs_to_many_property(options[:foreign_key]) if options[:storing_keys]
|
189
|
+
define_has_and_belongs_to_many_views(name, options)
|
190
|
+
define_has_and_belongs_to_many_getter(name, options)
|
191
|
+
define_has_and_belongs_to_many_setter_add(name, options)
|
192
|
+
define_has_and_belongs_to_many_setter_remove(name, options)
|
193
|
+
define_has_and_belongs_to_many_setter_remove_all(name, options)
|
194
|
+
define_has_and_belongs_to_many_count(name, options)
|
195
|
+
define_has_and_belongs_to_many_after_destroy_cleanup(name, options)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -2,24 +2,14 @@ module SimplyStored
|
|
2
2
|
module Couch
|
3
3
|
module HasMany
|
4
4
|
def has_many(name, options = {})
|
5
|
+
check_existing_properties(name, SimplyStored::Couch::HasMany::Property)
|
5
6
|
properties << SimplyStored::Couch::HasMany::Property.new(self, name, options)
|
6
7
|
end
|
7
8
|
|
8
9
|
def define_has_many_getter(name, options)
|
9
10
|
define_method(name) do |*args|
|
10
11
|
local_options = args.first && args.first.is_a?(Hash) && args.first
|
11
|
-
|
12
|
-
local_options.assert_valid_keys(:force_reload, :with_deleted, :limit, :order)
|
13
|
-
forced_reload = local_options.delete(:force_reload)
|
14
|
-
with_deleted = local_options[:with_deleted]
|
15
|
-
limit = local_options[:limit]
|
16
|
-
descending = (local_options[:order] == :desc) ? true : false
|
17
|
-
else
|
18
|
-
forced_reload = false
|
19
|
-
with_deleted = false
|
20
|
-
limit = nil
|
21
|
-
descending = false
|
22
|
-
end
|
12
|
+
forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
|
23
13
|
|
24
14
|
cached_results = send("_get_cached_#{name}")
|
25
15
|
cache_key = _cache_key_for(local_options)
|
@@ -131,22 +121,6 @@ module SimplyStored
|
|
131
121
|
end
|
132
122
|
end
|
133
123
|
|
134
|
-
def define_cache_accessors(name, options)
|
135
|
-
define_method "_get_cached_#{name}" do
|
136
|
-
instance_variable_get("@#{name}") || {}
|
137
|
-
end
|
138
|
-
|
139
|
-
define_method "_set_cached_#{name}" do |value, cache_key|
|
140
|
-
cached = send("_get_cached_#{name}")
|
141
|
-
cached[cache_key] = value
|
142
|
-
instance_variable_set("@#{name}", cached)
|
143
|
-
end
|
144
|
-
|
145
|
-
define_method "_cache_key_for" do |opt|
|
146
|
-
opt.blank? ? :all : opt.to_s
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
124
|
def set_parent_has_many_association_object(parent, child_collection)
|
151
125
|
child_collection.each do |child|
|
152
126
|
if child.respond_to?("#{parent.class.name.to_s.singularize.downcase}=")
|
@@ -155,8 +129,7 @@ module SimplyStored
|
|
155
129
|
end
|
156
130
|
end
|
157
131
|
|
158
|
-
class Property
|
159
|
-
attr_reader :name, :options
|
132
|
+
class Property < SimplyStored::Couch::AssociationProperty
|
160
133
|
|
161
134
|
def initialize(owner_clazz, name, options = {})
|
162
135
|
options = {
|
@@ -171,13 +144,13 @@ module SimplyStored
|
|
171
144
|
|
172
145
|
if options[:through]
|
173
146
|
owner_clazz.class_eval do
|
174
|
-
|
147
|
+
_define_cache_accessors(name, options)
|
175
148
|
define_has_many_through_getter(name, options, options[:through])
|
176
149
|
define_has_many_count(name, options, options[:through])
|
177
150
|
end
|
178
151
|
else
|
179
152
|
owner_clazz.class_eval do
|
180
|
-
|
153
|
+
_define_cache_accessors(name, options)
|
181
154
|
define_has_many_getter(name, options)
|
182
155
|
define_has_many_setter_add(name, options)
|
183
156
|
define_has_many_setter_remove(name, options)
|
@@ -187,25 +160,7 @@ module SimplyStored
|
|
187
160
|
end
|
188
161
|
end
|
189
162
|
|
190
|
-
def dirty?(object)
|
191
|
-
false
|
192
|
-
end
|
193
|
-
|
194
|
-
def build(object, json)
|
195
|
-
end
|
196
|
-
|
197
|
-
def serialize(json, object)
|
198
|
-
end
|
199
|
-
alias :value :serialize
|
200
|
-
|
201
|
-
def supports_dirty?
|
202
|
-
false
|
203
|
-
end
|
204
|
-
|
205
|
-
def association?
|
206
|
-
true
|
207
|
-
end
|
208
163
|
end
|
209
164
|
end
|
210
165
|
end
|
211
|
-
end
|
166
|
+
end
|
@@ -2,6 +2,7 @@ module SimplyStored
|
|
2
2
|
module Couch
|
3
3
|
module HasOne
|
4
4
|
def has_one(name, options = {})
|
5
|
+
check_existing_properties(name, SimplyStored::Couch::HasOne::Property)
|
5
6
|
properties << SimplyStored::Couch::HasOne::Property.new(self, name, options)
|
6
7
|
end
|
7
8
|
|
@@ -33,14 +34,7 @@ module SimplyStored
|
|
33
34
|
def define_has_one_getter(name, options)
|
34
35
|
define_method(name) do |*args|
|
35
36
|
local_options = args.first && args.first.is_a?(Hash) && args.first
|
36
|
-
|
37
|
-
local_options.assert_valid_keys(:force_reload, :with_deleted)
|
38
|
-
forced_reload = local_options[:force_reload]
|
39
|
-
with_deleted = local_options[:with_deleted]
|
40
|
-
else
|
41
|
-
forced_reload = false
|
42
|
-
with_deleted = false
|
43
|
-
end
|
37
|
+
forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
|
44
38
|
|
45
39
|
if forced_reload || instance_variable_get("@#{name}").nil?
|
46
40
|
found_object = find_one_associated(options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key])
|
@@ -57,8 +51,7 @@ module SimplyStored
|
|
57
51
|
end
|
58
52
|
end
|
59
53
|
|
60
|
-
class Property
|
61
|
-
attr_reader :name, :options
|
54
|
+
class Property < SimplyStored::Couch::AssociationProperty
|
62
55
|
|
63
56
|
def initialize(owner_clazz, name, options = {})
|
64
57
|
options = {
|
@@ -76,25 +69,7 @@ module SimplyStored
|
|
76
69
|
end
|
77
70
|
end
|
78
71
|
|
79
|
-
def dirty?(object)
|
80
|
-
false
|
81
|
-
end
|
82
|
-
|
83
|
-
def build(object, json)
|
84
|
-
end
|
85
|
-
|
86
|
-
def serialize(json, object)
|
87
|
-
end
|
88
|
-
alias :value :serialize
|
89
|
-
|
90
|
-
def supports_dirty?
|
91
|
-
false
|
92
|
-
end
|
93
|
-
|
94
|
-
def association?
|
95
|
-
true
|
96
|
-
end
|
97
72
|
end
|
98
73
|
end
|
99
74
|
end
|
100
|
-
end
|
75
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SimplyStored
|
2
|
+
module Couch
|
3
|
+
module Properties
|
4
|
+
def check_existing_properties(name, type)
|
5
|
+
if properties.find{|property| name.to_sym == property.name.to_sym && property.class != type}
|
6
|
+
raise "Property with the name (#{name}) already defined"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|