simply_stored 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +2 -0
- data/README.md +13 -0
- data/lib/simply_stored/class_methods_base.rb +2 -2
- data/lib/simply_stored/couch/belongs_to.rb +16 -15
- data/lib/simply_stored/couch/has_many.rb +47 -45
- data/lib/simply_stored/couch/has_one.rb +18 -14
- data/lib/simply_stored/instance_methods.rb +5 -4
- data/lib/simply_stored/simpledb/associations.rb +109 -90
- data/lib/simply_stored/simpledb.rb +6 -2
- data/lib/simply_stored.rb +1 -1
- data/test/fixtures/couch.rb +8 -0
- data/test/simply_stored_couch_test.rb +19 -0
- metadata +3 -3
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -136,6 +136,19 @@ The supported associations are: belongs_to, has_one, has_many, and has_many :thr
|
|
136
136
|
post.user_count
|
137
137
|
# => 2
|
138
138
|
|
139
|
+
|
140
|
+
CouchDB - Custom Associations
|
141
|
+
|
142
|
+
class Document
|
143
|
+
include SimplyStored::Couch
|
144
|
+
|
145
|
+
belongs_to :creator, :class_name => "User"
|
146
|
+
belongs_to :updater, :class_name => "User"
|
147
|
+
end
|
148
|
+
|
149
|
+
d = Document.new
|
150
|
+
d.creator = User.first
|
151
|
+
|
139
152
|
|
140
153
|
CouchDB - Validations
|
141
154
|
=============
|
@@ -2,7 +2,7 @@ module SimplyStored
|
|
2
2
|
module ClassMethods
|
3
3
|
module Base
|
4
4
|
def get_class_from_name(klass_name)
|
5
|
-
klass_name.to_s.gsub('__','/').
|
5
|
+
klass_name.to_s.gsub('__','/').classify.constantize
|
6
6
|
end
|
7
7
|
|
8
8
|
def foreign_key
|
@@ -28,4 +28,4 @@ module SimplyStored
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -3,7 +3,7 @@ module SimplyStored
|
|
3
3
|
module Couch
|
4
4
|
module BelongsTo
|
5
5
|
|
6
|
-
def belongs_to(name)
|
6
|
+
def belongs_to(name, options = {})
|
7
7
|
map_definition_without_deleted = <<-eos
|
8
8
|
function(doc) {
|
9
9
|
if (doc['ruby_class'] == '#{self.to_s}' && doc['#{name.to_s}_id'] != null) {
|
@@ -43,32 +43,37 @@ module SimplyStored
|
|
43
43
|
:type => "custom",
|
44
44
|
:include_docs => true
|
45
45
|
|
46
|
-
properties << SimplyStored::Couch::BelongsTo::Property.new(self, name)
|
46
|
+
properties << SimplyStored::Couch::BelongsTo::Property.new(self, name, options)
|
47
47
|
end
|
48
48
|
|
49
49
|
class Property #:nodoc:
|
50
50
|
attr_accessor :name, :options
|
51
51
|
|
52
52
|
def initialize(owner_clazz, name, options = {})
|
53
|
-
@name
|
54
|
-
|
53
|
+
@name = name
|
54
|
+
@options = {
|
55
|
+
:class_name => name.to_s.singularize.camelize
|
56
|
+
}.update(options)
|
57
|
+
|
58
|
+
@options.assert_valid_keys(:class_name)
|
59
|
+
|
55
60
|
owner_clazz.class_eval do
|
56
61
|
attr_reader "#{name}_id"
|
57
62
|
attr_accessor "#{name}_id_was"
|
58
63
|
property "#{name}_id"
|
59
64
|
|
60
65
|
define_method name do |*args|
|
61
|
-
|
62
|
-
|
63
|
-
forced_reload =
|
64
|
-
with_deleted =
|
66
|
+
local_options = args.last.is_a?(Hash) ? args.last : {}
|
67
|
+
local_options.assert_valid_keys(:force_reload, :with_deleted)
|
68
|
+
forced_reload = local_options[:force_reload] || false
|
69
|
+
with_deleted = local_options[:with_deleted] || false
|
65
70
|
|
66
71
|
return instance_variable_get("@#{name}") unless instance_variable_get("@#{name}").nil? or forced_reload
|
67
|
-
instance_variable_set("@#{name}", send("#{name}_id").present? ? Object.const_get(
|
72
|
+
instance_variable_set("@#{name}", send("#{name}_id").present? ? Object.const_get(self.class._find_property(name).options[:class_name]).find(send("#{name}_id"), :with_deleted => with_deleted) : nil)
|
68
73
|
end
|
69
74
|
|
70
75
|
define_method "#{name}=" do |value|
|
71
|
-
klass = self.class.get_class_from_name(name)
|
76
|
+
klass = self.class.get_class_from_name(self.class._find_property(name).options[:class_name])
|
72
77
|
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.nil? || value.is_a?(klass)
|
73
78
|
|
74
79
|
instance_variable_set("@#{name}", value)
|
@@ -107,11 +112,7 @@ module SimplyStored
|
|
107
112
|
json["#{name}_id"] = object.send("#{name}_id") if object.send("#{name}_id")
|
108
113
|
end
|
109
114
|
alias :value :serialize
|
110
|
-
|
111
|
-
def item_class_name
|
112
|
-
@name.to_s.camelize
|
113
|
-
end
|
114
|
-
|
115
|
+
|
115
116
|
def association?
|
116
117
|
true
|
117
118
|
end
|
@@ -5,15 +5,15 @@ module SimplyStored
|
|
5
5
|
properties << SimplyStored::Couch::HasMany::Property.new(self, name, options)
|
6
6
|
end
|
7
7
|
|
8
|
-
def define_has_many_getter(name)
|
8
|
+
def define_has_many_getter(name, options)
|
9
9
|
define_method(name) do |*args|
|
10
|
-
|
11
|
-
if
|
12
|
-
|
13
|
-
forced_reload =
|
14
|
-
with_deleted =
|
15
|
-
limit =
|
16
|
-
descending = (
|
10
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
11
|
+
if local_options
|
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
17
|
else
|
18
18
|
forced_reload = false
|
19
19
|
with_deleted = false
|
@@ -22,25 +22,25 @@ module SimplyStored
|
|
22
22
|
end
|
23
23
|
|
24
24
|
cached_results = cached_results = send("_get_cached_#{name}")
|
25
|
-
cache_key = _cache_key_for(
|
25
|
+
cache_key = _cache_key_for(local_options)
|
26
26
|
if forced_reload || cached_results[cache_key].nil?
|
27
|
-
cached_results[cache_key] = find_associated(
|
27
|
+
cached_results[cache_key] = find_associated(options[:class_name], self.class, :with_deleted => with_deleted, :limit => limit, :descending => descending, :foreign_key => options[:foreign_key])
|
28
28
|
instance_variable_set("@#{name}", cached_results)
|
29
29
|
end
|
30
30
|
cached_results[cache_key]
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def define_has_many_through_getter(name, through)
|
34
|
+
def define_has_many_through_getter(name, options, through)
|
35
35
|
raise ArgumentError, "no such relation: #{self} - #{through}" unless instance_methods.map(&:to_sym).include?(through.to_sym)
|
36
36
|
|
37
37
|
define_method(name) do |*args|
|
38
|
-
|
39
|
-
if
|
40
|
-
|
41
|
-
forced_reload =
|
42
|
-
with_deleted =
|
43
|
-
limit =
|
38
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
39
|
+
if local_options
|
40
|
+
local_options.assert_valid_keys(:force_reload, :with_deleted, :limit)
|
41
|
+
forced_reload = local_options[:force_reload]
|
42
|
+
with_deleted = local_options[:with_deleted]
|
43
|
+
limit = local_options[:limit]
|
44
44
|
else
|
45
45
|
forced_reload = false
|
46
46
|
with_deleted = false
|
@@ -48,12 +48,12 @@ module SimplyStored
|
|
48
48
|
end
|
49
49
|
|
50
50
|
cached_results = send("_get_cached_#{name}")
|
51
|
-
cache_key = _cache_key_for(
|
51
|
+
cache_key = _cache_key_for(local_options)
|
52
52
|
|
53
53
|
if forced_reload || cached_results[cache_key].nil?
|
54
54
|
|
55
55
|
# there is probably a faster way to query this
|
56
|
-
intermediate_objects = find_associated(through, self.class, :with_deleted => with_deleted, :limit => limit)
|
56
|
+
intermediate_objects = find_associated(through, self.class, :with_deleted => with_deleted, :limit => limit, :foreign_key => options[:foreign_key])
|
57
57
|
|
58
58
|
through_objects = intermediate_objects.map do |intermediate_object|
|
59
59
|
intermediate_object.send(name.to_s.singularize.underscore, :with_deleted => with_deleted)
|
@@ -65,7 +65,7 @@ module SimplyStored
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
def define_has_many_setter_add(name)
|
68
|
+
def define_has_many_setter_add(name, options)
|
69
69
|
define_method("add_#{name.to_s.singularize}") do |value|
|
70
70
|
klass = self.class.get_class_from_name(name)
|
71
71
|
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
|
@@ -79,15 +79,15 @@ module SimplyStored
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
def define_has_many_setter_remove(name)
|
82
|
+
def define_has_many_setter_remove(name, options)
|
83
83
|
define_method "remove_#{name.to_s.singularize}" do |value|
|
84
84
|
klass = self.class.get_class_from_name(name)
|
85
85
|
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
|
86
86
|
raise ArgumentError, "cannot remove not mine" unless value.send(self.class.foreign_key.to_sym) == id
|
87
87
|
|
88
|
-
if
|
88
|
+
if options[:dependent] == :destroy
|
89
89
|
value.destroy
|
90
|
-
elsif
|
90
|
+
elsif options[:dependent] == :ignore
|
91
91
|
# skip
|
92
92
|
else # nullify
|
93
93
|
value.send("#{self.class.foreign_key}=", nil)
|
@@ -100,7 +100,7 @@ module SimplyStored
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
def define_has_many_setter_remove_all(name)
|
103
|
+
def define_has_many_setter_remove_all(name, options)
|
104
104
|
define_method "remove_all_#{name}" do
|
105
105
|
all = send("#{name}", :force_reload => true)
|
106
106
|
|
@@ -110,27 +110,27 @@ module SimplyStored
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
-
def define_has_many_count(name, through = nil)
|
113
|
+
def define_has_many_count(name, options, through = nil)
|
114
114
|
method_name = name.to_s.singularize.underscore + "_count"
|
115
115
|
define_method(method_name) do |*args|
|
116
|
-
|
117
|
-
if
|
118
|
-
|
119
|
-
forced_reload =
|
120
|
-
with_deleted =
|
116
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
117
|
+
if local_options
|
118
|
+
local_options.assert_valid_keys(:force_reload, :with_deleted)
|
119
|
+
forced_reload = local_options[:force_reload]
|
120
|
+
with_deleted = local_options[:with_deleted]
|
121
121
|
else
|
122
122
|
forced_reload = false
|
123
123
|
with_deleted = false
|
124
124
|
end
|
125
125
|
|
126
126
|
if forced_reload || instance_variable_get("@#{method_name}").nil?
|
127
|
-
instance_variable_set("@#{method_name}", count_associated(through ||
|
127
|
+
instance_variable_set("@#{method_name}", count_associated(through || options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key]))
|
128
128
|
end
|
129
129
|
instance_variable_get("@#{method_name}")
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
-
def define_cache_accessors(name)
|
133
|
+
def define_cache_accessors(name, options)
|
134
134
|
define_method "_get_cached_#{name}" do
|
135
135
|
instance_variable_get("@#{name}") || {}
|
136
136
|
end
|
@@ -141,8 +141,8 @@ module SimplyStored
|
|
141
141
|
instance_variable_set("@#{name}", cached)
|
142
142
|
end
|
143
143
|
|
144
|
-
define_method "_cache_key_for" do |
|
145
|
-
|
144
|
+
define_method "_cache_key_for" do |opt|
|
145
|
+
opt.blank? ? :all : opt.to_s
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
@@ -152,26 +152,28 @@ module SimplyStored
|
|
152
152
|
def initialize(owner_clazz, name, options = {})
|
153
153
|
options = {
|
154
154
|
:dependent => :nullify,
|
155
|
-
:through => nil
|
155
|
+
:through => nil,
|
156
|
+
:class_name => name.to_s.singularize.camelize,
|
157
|
+
:foreign_key => owner_clazz.name.singularize.underscore.foreign_key
|
156
158
|
}.update(options)
|
157
159
|
@name, @options = name, options
|
158
160
|
|
159
|
-
options.assert_valid_keys(:dependent, :through)
|
161
|
+
options.assert_valid_keys(:dependent, :through, :class_name, :foreign_key)
|
160
162
|
|
161
163
|
if options[:through]
|
162
164
|
owner_clazz.class_eval do
|
163
|
-
define_cache_accessors(name)
|
164
|
-
define_has_many_through_getter(name, options[:through])
|
165
|
-
define_has_many_count(name, options[:through])
|
165
|
+
define_cache_accessors(name, options)
|
166
|
+
define_has_many_through_getter(name, options, options[:through])
|
167
|
+
define_has_many_count(name, options, options[:through])
|
166
168
|
end
|
167
169
|
else
|
168
170
|
owner_clazz.class_eval do
|
169
|
-
define_cache_accessors(name)
|
170
|
-
define_has_many_getter(name)
|
171
|
-
define_has_many_setter_add(name)
|
172
|
-
define_has_many_setter_remove(name)
|
173
|
-
define_has_many_setter_remove_all(name)
|
174
|
-
define_has_many_count(name)
|
171
|
+
define_cache_accessors(name, options)
|
172
|
+
define_has_many_getter(name, options)
|
173
|
+
define_has_many_setter_add(name, options)
|
174
|
+
define_has_many_setter_remove(name, options)
|
175
|
+
define_has_many_setter_remove_all(name, options)
|
176
|
+
define_has_many_count(name, options)
|
175
177
|
end
|
176
178
|
end
|
177
179
|
end
|
@@ -5,9 +5,9 @@ module SimplyStored
|
|
5
5
|
properties << SimplyStored::Couch::HasOne::Property.new(self, name, options)
|
6
6
|
end
|
7
7
|
|
8
|
-
def define_has_one_setter(name)
|
8
|
+
def define_has_one_setter(name, options)
|
9
9
|
define_method("#{name}=") do |value|
|
10
|
-
klass = self.class.get_class_from_name(name)
|
10
|
+
klass = self.class.get_class_from_name(self.class._find_property(name).options[:class_name])
|
11
11
|
raise ArgumentError, "expected #{klass} got #{value.class}" unless value.nil? || value.is_a?(klass)
|
12
12
|
old_value = send("#{name}", :force_reload => true)
|
13
13
|
if value.nil?
|
@@ -19,7 +19,7 @@ module SimplyStored
|
|
19
19
|
end
|
20
20
|
|
21
21
|
if old_value
|
22
|
-
if
|
22
|
+
if options[:dependent] == :destroy
|
23
23
|
old_value.destroy
|
24
24
|
else
|
25
25
|
old_value.send("#{self.class.foreign_property}=", nil)
|
@@ -30,20 +30,20 @@ module SimplyStored
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def define_has_one_getter(name)
|
33
|
+
def define_has_one_getter(name, options)
|
34
34
|
define_method(name) do |*args|
|
35
|
-
|
36
|
-
if
|
37
|
-
|
38
|
-
forced_reload =
|
39
|
-
with_deleted =
|
35
|
+
local_options = args.first && args.first.is_a?(Hash) && args.first
|
36
|
+
if local_options
|
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
40
|
else
|
41
41
|
forced_reload = false
|
42
42
|
with_deleted = false
|
43
43
|
end
|
44
44
|
|
45
45
|
if forced_reload || instance_variable_get("@#{name}").nil?
|
46
|
-
instance_variable_set("@#{name}", find_one_associated(
|
46
|
+
instance_variable_set("@#{name}", find_one_associated(options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key]))
|
47
47
|
end
|
48
48
|
instance_variable_get("@#{name}")
|
49
49
|
end
|
@@ -53,14 +53,18 @@ module SimplyStored
|
|
53
53
|
attr_reader :name, :options
|
54
54
|
|
55
55
|
def initialize(owner_clazz, name, options = {})
|
56
|
-
options = {
|
56
|
+
options = {
|
57
|
+
:dependent => :nullify,
|
58
|
+
:class_name => name.to_s.singularize.camelize,
|
59
|
+
:foreign_key => owner_clazz.name.singularize.underscore.foreign_key
|
60
|
+
}.update(options)
|
57
61
|
@name, @options = name, options
|
58
62
|
|
59
|
-
options.assert_valid_keys(:dependent)
|
63
|
+
options.assert_valid_keys(:dependent, :class_name, :foreign_key)
|
60
64
|
|
61
65
|
owner_clazz.class_eval do
|
62
|
-
define_has_one_getter(name)
|
63
|
-
define_has_one_setter(name)
|
66
|
+
define_has_one_getter(name, options)
|
67
|
+
define_has_one_setter(name, options)
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
@@ -72,8 +72,8 @@ module SimplyStored
|
|
72
72
|
retry_count = 0
|
73
73
|
begin
|
74
74
|
blk.call
|
75
|
-
rescue RestClient::Conflict => e
|
76
|
-
if self.class.auto_conflict_resolution_on_save && retry_count < max_retries && try_to_merge_conflict
|
75
|
+
rescue RestClient::Exception, RestClient::Conflict => e
|
76
|
+
if (e.http_code == 409 || e.is_a?(RestClient::Conflict)) && self.class.auto_conflict_resolution_on_save && retry_count < max_retries && try_to_merge_conflict
|
77
77
|
retry_count += 1
|
78
78
|
retry
|
79
79
|
else
|
@@ -175,6 +175,7 @@ module SimplyStored
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def find_associated(from, to, options = {})
|
178
|
+
foreign_key = options.delete(:foreign_key).gsub(/_id$/, '')
|
178
179
|
view_options = {}
|
179
180
|
view_options[:reduce] = false
|
180
181
|
view_options[:descending] = options[:descending] if options[:descending]
|
@@ -189,11 +190,11 @@ module SimplyStored
|
|
189
190
|
if options[:with_deleted]
|
190
191
|
CouchPotato.database.view(
|
191
192
|
self.class.get_class_from_name(from).send(
|
192
|
-
"association_#{from.to_s.singularize.underscore}_belongs_to_#{
|
193
|
+
"association_#{from.to_s.singularize.underscore}_belongs_to_#{foreign_key}_with_deleted", view_options))
|
193
194
|
else
|
194
195
|
CouchPotato.database.view(
|
195
196
|
self.class.get_class_from_name(from).send(
|
196
|
-
"association_#{from.to_s.singularize.underscore}_belongs_to_#{
|
197
|
+
"association_#{from.to_s.singularize.underscore}_belongs_to_#{foreign_key}", view_options))
|
197
198
|
end
|
198
199
|
end
|
199
200
|
|
@@ -6,98 +6,112 @@ module SimplyStored
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
def belongs_to(
|
10
|
-
|
11
|
-
|
9
|
+
def belongs_to(name, options = nil)
|
10
|
+
options = {
|
11
|
+
:class_name => self.formalize_class_name(name),
|
12
|
+
:foreign_key => "#{name}_id"
|
13
|
+
}.update(options || {})
|
14
|
+
define_belongs_to_getter(name, options)
|
15
|
+
define_belongs_to_setter(name, options)
|
12
16
|
end
|
13
17
|
|
14
|
-
def has_one(
|
18
|
+
def has_one(name, options = {})
|
15
19
|
options = {
|
16
20
|
:clear => :nullify, # or :destroy
|
17
|
-
:dependent => :nullify # or :destroy
|
21
|
+
:dependent => :nullify, # or :destroy
|
22
|
+
:class_name => self.formalize_class_name(name),
|
23
|
+
:foreign_key => self.foreign_key
|
18
24
|
}.update(options)
|
19
25
|
|
20
|
-
define_has_one_getter(
|
21
|
-
define_has_one_setter(
|
22
|
-
define_has_one_dependent_clearing(
|
26
|
+
define_has_one_getter(name, options)
|
27
|
+
define_has_one_setter(name, options)
|
28
|
+
define_has_one_dependent_clearing(name, options)
|
23
29
|
end
|
24
30
|
|
25
|
-
def has_many(
|
31
|
+
def has_many(name, options = {})
|
26
32
|
options = {
|
27
33
|
:clear => :nullify, # or :destroy
|
28
|
-
:dependent => :nullify # or :destroy
|
34
|
+
:dependent => :nullify, # or :destroy
|
35
|
+
:class_name => self.formalize_class_name(name.to_s.singularize),
|
36
|
+
:foreign_key => self.foreign_key
|
29
37
|
}.update(options)
|
30
38
|
|
31
|
-
define_has_many_getter(
|
32
|
-
define_has_many_setter_add(
|
33
|
-
define_has_many_setter_remove(
|
34
|
-
define_has_many_setter_remove_all(
|
35
|
-
define_has_many_dependent_clearing(
|
39
|
+
define_has_many_getter(name, options)
|
40
|
+
define_has_many_setter_add(name, options)
|
41
|
+
define_has_many_setter_remove(name, options)
|
42
|
+
define_has_many_setter_remove_all(name, options)
|
43
|
+
define_has_many_dependent_clearing(name, options)
|
36
44
|
end
|
37
45
|
|
38
|
-
def define_belongs_to_getter(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
46
|
+
def define_belongs_to_getter(name, options)
|
47
|
+
foreign_key_column = options[:foreign_key]
|
48
|
+
define_method(name.to_s) do
|
49
|
+
klass = options[:class_name].constantize
|
50
|
+
cached_version = instance_variable_get("@_cached_belongs_to_#{name}")
|
51
|
+
if cached_version.nil? and self[foreign_key_column].present?
|
52
|
+
cached_version = klass.find(self.send(foreign_key_column), :auto_load => true)
|
53
|
+
instance_variable_set("@_cached_belongs_to_#{name}", cached_version)
|
45
54
|
end
|
46
55
|
cached_version
|
47
56
|
end
|
48
57
|
end
|
49
58
|
|
50
|
-
def define_belongs_to_setter(
|
51
|
-
|
52
|
-
|
59
|
+
def define_belongs_to_setter(name, options)
|
60
|
+
foreign_key_column = options[:foreign_key]
|
61
|
+
define_method("#{name}=") do |val|
|
62
|
+
klass = options[:class_name].constantize
|
53
63
|
raise ArgumentError, "expected #{klass} got #{val.class}" unless val.is_a?(klass)
|
54
|
-
self.send("#{
|
55
|
-
instance_variable_set("@_cached_belongs_to_#{
|
64
|
+
self.send("#{foreign_key_column}=", val.id)
|
65
|
+
instance_variable_set("@_cached_belongs_to_#{name}", val)
|
56
66
|
end
|
57
67
|
end
|
58
68
|
|
59
|
-
def define_has_one_getter(
|
60
|
-
|
61
|
-
|
62
|
-
|
69
|
+
def define_has_one_getter(name, options)
|
70
|
+
foreign_key_column = options[:foreign_key]
|
71
|
+
define_method(name.to_s) do
|
72
|
+
klass = options[:class_name].constantize
|
73
|
+
cached_version = instance_variable_get("@_cached_has_one_#{name}")
|
63
74
|
if cached_version
|
64
|
-
|
75
|
+
cached_version
|
65
76
|
else
|
66
|
-
cached_version = klass.send("find_by_#{
|
67
|
-
instance_variable_set("@_cached_has_one_#{
|
68
|
-
|
77
|
+
cached_version = klass.send("find_by_#{foreign_key_column}".to_sym, self.id, {:auto_load => true})
|
78
|
+
instance_variable_set("@_cached_has_one_#{name}", cached_version)
|
79
|
+
cached_version
|
69
80
|
end
|
70
81
|
end
|
71
82
|
end
|
72
83
|
|
73
|
-
def define_has_one_setter(
|
74
|
-
|
75
|
-
|
84
|
+
def define_has_one_setter(name, options)
|
85
|
+
foreign_key_column = options[:foreign_key]
|
86
|
+
define_method("#{name}=") do |val|
|
87
|
+
klass = options[:class_name].constantize
|
76
88
|
raise ArgumentError, "expected #{klass} got #{val.class}" unless val.is_a?(klass)
|
77
89
|
|
78
90
|
# clear old
|
79
|
-
old = self.send("#{
|
80
|
-
old.send("#{
|
91
|
+
old = self.send("#{name}")
|
92
|
+
old.send("#{foreign_key_column}=", nil) if old && options[:clear] == :nullify
|
81
93
|
old.delete if old && options[:clear] == :destroy
|
82
94
|
|
83
95
|
# store new
|
84
|
-
val.send("#{
|
85
|
-
instance_variable_set("@_cached_has_one_#{
|
96
|
+
val.send("#{foreign_key_column}=", self.id)
|
97
|
+
instance_variable_set("@_cached_has_one_#{name}", val)
|
86
98
|
end
|
87
99
|
end
|
88
100
|
|
89
|
-
def define_has_one_dependent_clearing(
|
101
|
+
def define_has_one_dependent_clearing(name, options)
|
90
102
|
# add method to list of methods to run when deleted
|
91
103
|
@_clear_dependents_after_delete_methods ||= []
|
92
|
-
@_clear_dependents_after_delete_methods << "has_one_clear_#{
|
104
|
+
@_clear_dependents_after_delete_methods << "has_one_clear_#{name}_after_destroy"
|
93
105
|
|
94
106
|
# define actual clearing/deleting
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
107
|
+
foreign_key_column = options[:foreign_key]
|
108
|
+
define_method("has_one_clear_#{name}_after_destroy") do
|
109
|
+
klass = options[:class_name].constantize
|
110
|
+
dependent = klass.send("find_by_#{foreign_key_column}".to_sym, self.id)
|
111
|
+
case options[:dependent]
|
112
|
+
when :nullify then
|
113
|
+
dependent.send("#{foreign_key_column}=", nil) if dependent
|
114
|
+
when :destroy then
|
101
115
|
dependent.delete if dependent
|
102
116
|
else
|
103
117
|
raise ArgumentError, "unknown dependent method: #{options[:dependent].inspect}"
|
@@ -105,76 +119,81 @@ module SimplyStored
|
|
105
119
|
end
|
106
120
|
end
|
107
121
|
|
108
|
-
def define_has_many_getter(
|
109
|
-
|
110
|
-
|
111
|
-
|
122
|
+
def define_has_many_getter(name, options)
|
123
|
+
foreign_key_column = options[:foreign_key]
|
124
|
+
define_method(name.to_s) do
|
125
|
+
klass = options[:class_name].constantize
|
126
|
+
cached_version = instance_variable_get("@_cached_has_many_#{name}")
|
112
127
|
if cached_version
|
113
|
-
|
128
|
+
cached_version
|
114
129
|
else
|
115
|
-
cached_version = klass.send("find_all_by_#{
|
116
|
-
instance_variable_set("@_cached_has_many_#{
|
117
|
-
|
130
|
+
cached_version = klass.send("find_all_by_#{foreign_key_column}".to_sym, self.id, {:auto_load => true})
|
131
|
+
instance_variable_set("@_cached_has_many_#{name}", cached_version)
|
132
|
+
cached_version
|
118
133
|
end
|
119
134
|
end
|
120
135
|
end
|
121
136
|
|
122
|
-
def define_has_many_setter_add(
|
123
|
-
|
124
|
-
|
137
|
+
def define_has_many_setter_add(name, options)
|
138
|
+
foreign_key_column = options[:foreign_key]
|
139
|
+
define_method("add_#{name.to_s.singularize}") do |val|
|
140
|
+
klass = options[:class_name].constantize
|
125
141
|
raise ArgumentError, "expected #{klass} got #{val.class}" unless val.is_a?(klass)
|
126
|
-
val.send("#{
|
142
|
+
val.send("#{foreign_key_column}=", self.id)
|
127
143
|
val.save(false)
|
128
|
-
cached_version = instance_variable_get("@_cached_has_many_#{
|
129
|
-
instance_variable_set("@_cached_has_many_#{
|
144
|
+
cached_version = instance_variable_get("@_cached_has_many_#{name}") || []
|
145
|
+
instance_variable_set("@_cached_has_many_#{name}", cached_version << val)
|
130
146
|
end
|
131
147
|
end
|
132
148
|
|
133
|
-
def define_has_many_setter_remove(
|
134
|
-
|
135
|
-
|
149
|
+
def define_has_many_setter_remove(name, options)
|
150
|
+
foreign_key_column = options[:foreign_key]
|
151
|
+
define_method("remove_#{name.to_s.singularize}") do |val|
|
152
|
+
klass = options[:class_name].constantize
|
136
153
|
raise ArgumentError, "expected #{klass} got #{val.class}" unless val.is_a?(klass)
|
137
|
-
raise ArgumentError, "cannot remove not mine" unless val.send(
|
138
|
-
|
139
|
-
|
154
|
+
raise ArgumentError, "cannot remove not mine" unless val.send(foreign_key_column) == self.id
|
155
|
+
case options[:clear]
|
156
|
+
when :nullify then
|
157
|
+
val.send("#{foreign_key_column}=", nil)
|
140
158
|
val.save(false)
|
141
|
-
|
159
|
+
when :destroy then
|
142
160
|
val.delete
|
143
161
|
else
|
144
162
|
raise "Unknown option for clear: #{option[:clear]}"
|
145
163
|
end
|
146
|
-
cached_version = instance_variable_get("@_cached_has_many_#{
|
147
|
-
instance_variable_set("@_cached_has_many_#{
|
164
|
+
cached_version = instance_variable_get("@_cached_has_many_#{name}") || []
|
165
|
+
instance_variable_set("@_cached_has_many_#{name}", cached_version.delete_if{|x| x.id == val.id})
|
148
166
|
end
|
149
167
|
end
|
150
168
|
|
151
|
-
def define_has_many_setter_remove_all(
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
all = klass.send("find_all_by_#{
|
156
|
-
|
169
|
+
def define_has_many_setter_remove_all(name, options)
|
170
|
+
foreign_key_column = options[:foreign_key]
|
171
|
+
define_method("remove_all_#{name}") do
|
172
|
+
klass = options[:class_name].constantize
|
173
|
+
all = klass.send("find_all_by_#{foreign_key_column}".to_sym, self.id)
|
157
174
|
all.each do |item|
|
158
|
-
self.send("remove_#{
|
175
|
+
self.send("remove_#{name.to_s.singularize}", item)
|
159
176
|
end
|
160
|
-
instance_variable_set("@_cached_has_many_#{
|
177
|
+
instance_variable_set("@_cached_has_many_#{name}", [])
|
161
178
|
end
|
162
179
|
end
|
163
180
|
|
164
|
-
def define_has_many_dependent_clearing(
|
181
|
+
def define_has_many_dependent_clearing(name, options)
|
165
182
|
# add method to list of methods to run when deleted
|
166
183
|
@_clear_dependents_after_delete_methods ||= []
|
167
|
-
@_clear_dependents_after_delete_methods << "has_many_clear_#{
|
184
|
+
@_clear_dependents_after_delete_methods << "has_many_clear_#{name}_after_destroy"
|
168
185
|
|
169
186
|
# define actual clearing/deleting
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
187
|
+
foreign_key_column = options[:foreign_key]
|
188
|
+
define_method("has_many_clear_#{name}_after_destroy") do
|
189
|
+
klass = options[:class_name].constantize
|
190
|
+
dependents = klass.send("find_all_by_#{foreign_key_column}".to_sym, self.id)
|
191
|
+
case options[:dependent]
|
192
|
+
when :nullify then
|
174
193
|
dependents.each do |dependent|
|
175
|
-
dependent.send("#{
|
194
|
+
dependent.send("#{foreign_key_column}=", nil)
|
176
195
|
end
|
177
|
-
|
196
|
+
when :destroy then
|
178
197
|
dependents.each do |dependent|
|
179
198
|
dependent.delete
|
180
199
|
end
|
@@ -193,4 +212,4 @@ module SimplyStored
|
|
193
212
|
end
|
194
213
|
end
|
195
214
|
end
|
196
|
-
end
|
215
|
+
end
|
@@ -206,7 +206,11 @@ module SimplyStored
|
|
206
206
|
end
|
207
207
|
|
208
208
|
def self.get_class_from_name(klass_name)
|
209
|
-
klass_name.to_s.gsub('__','/').
|
209
|
+
klass_name.to_s.gsub('__','/').classify.constantize
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.formalize_class_name(name)
|
213
|
+
name.to_s.gsub('__','/').gsub('__','::').classify
|
210
214
|
end
|
211
215
|
end
|
212
|
-
end
|
216
|
+
end
|
data/lib/simply_stored.rb
CHANGED
@@ -4,7 +4,7 @@ require File.expand_path(File.dirname(__FILE__) + '/simply_stored/storage')
|
|
4
4
|
require File.expand_path(File.dirname(__FILE__) + '/simply_stored/class_methods_base')
|
5
5
|
|
6
6
|
module SimplyStored
|
7
|
-
VERSION = '0.3.
|
7
|
+
VERSION = '0.3.4'
|
8
8
|
class Error < RuntimeError; end
|
9
9
|
class RecordNotFound < RuntimeError; end
|
10
10
|
end
|
data/test/fixtures/couch.rb
CHANGED
@@ -10,6 +10,7 @@ class User
|
|
10
10
|
has_many :strict_posts
|
11
11
|
has_many :hemorrhoids
|
12
12
|
has_many :pains, :through => :hemorrhoids
|
13
|
+
has_many :docs, :class_name => "Document", :foreign_key => "editor_id"
|
13
14
|
|
14
15
|
view :by_name_and_created_at, :key => [:name, :created_at]
|
15
16
|
end
|
@@ -44,6 +45,13 @@ class Category
|
|
44
45
|
validates_inclusion_of :name, :in => ["food", "drinks", "party"], :allow_blank => true
|
45
46
|
end
|
46
47
|
|
48
|
+
class Document
|
49
|
+
include SimplyStored::Couch
|
50
|
+
|
51
|
+
belongs_to :author, :class_name => "User"
|
52
|
+
belongs_to :editor, :class_name => "User"
|
53
|
+
end
|
54
|
+
|
47
55
|
class Tag
|
48
56
|
include SimplyStored::Couch
|
49
57
|
|
@@ -474,6 +474,25 @@ class CouchTest < Test::Unit::TestCase
|
|
474
474
|
assert_nil post.user
|
475
475
|
end
|
476
476
|
end
|
477
|
+
|
478
|
+
context "with aliased associations" do
|
479
|
+
should "allow different names for the same class" do
|
480
|
+
editor = User.create(:name => 'Editor', :title => 'Dr.')
|
481
|
+
author = User.create(:name => 'author', :title => 'Dr.')
|
482
|
+
assert_not_nil editor.id, editor.errors.inspect
|
483
|
+
assert_not_nil author.id, author.errors.inspect
|
484
|
+
|
485
|
+
doc = Document.create(:editor => editor, :author => author)
|
486
|
+
doc.save!
|
487
|
+
assert_equal editor.id, doc.editor_id
|
488
|
+
assert_equal author.id, doc.author_id
|
489
|
+
doc = Document.find(doc.id)
|
490
|
+
assert_not_nil doc.editor, doc.inspect
|
491
|
+
assert_not_nil doc.author
|
492
|
+
assert_equal editor.id, doc.editor.id
|
493
|
+
assert_equal author.id, doc.author.id
|
494
|
+
end
|
495
|
+
end
|
477
496
|
end
|
478
497
|
|
479
498
|
context "with has_many" do
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
version: 0.3.
|
8
|
+
- 4
|
9
|
+
version: 0.3.4
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mathias Meyer, Jonathan Weiss
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-06-17 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|