simply_stored 0.3.3 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Changelog
2
2
  =============
3
3
 
4
+ - Add support for associations not named after the class [akm]
5
+
4
6
  0.3.3
5
7
 
6
8
  - Fix association counting bug
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('__','/').gsub('__','::').classify.constantize
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, @options = name, options
54
- item_class_name = self.item_class_name
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
- options = args.last.is_a?(Hash) ? args.last : {}
62
- options.assert_valid_keys(:force_reload, :with_deleted)
63
- forced_reload = options[:force_reload] || false
64
- with_deleted = options[:with_deleted] || false
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(item_class_name).find(send("#{name}_id"), :with_deleted => with_deleted) : nil)
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
- options = args.first && args.first.is_a?(Hash) && args.first
11
- if options
12
- options.assert_valid_keys(:force_reload, :with_deleted, :limit, :order)
13
- forced_reload = options.delete(:force_reload)
14
- with_deleted = options[:with_deleted]
15
- limit = options[:limit]
16
- descending = (options[:order] == :desc) ? true : false
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(options)
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(name, self.class, :with_deleted => with_deleted, :limit => limit, :descending => descending)
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
- options = args.first && args.first.is_a?(Hash) && args.first
39
- if options
40
- options.assert_valid_keys(:force_reload, :with_deleted, :limit)
41
- forced_reload = options[:force_reload]
42
- with_deleted = options[:with_deleted]
43
- limit = options[: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(options)
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 self.class._find_property(name).options[:dependent] == :destroy
88
+ if options[:dependent] == :destroy
89
89
  value.destroy
90
- elsif self.class._find_property(name).options[:dependent] == :ignore
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
- options = args.first && args.first.is_a?(Hash) && args.first
117
- if options
118
- options.assert_valid_keys(:force_reload, :with_deleted)
119
- forced_reload = options[:force_reload]
120
- with_deleted = options[: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 || name, self.class, :with_deleted => with_deleted))
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 |options|
145
- options.blank? ? :all : options.to_s
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 self.class._find_property(name).options[:dependent] == :destroy
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
- options = args.first && args.first.is_a?(Hash) && args.first
36
- if options
37
- options.assert_valid_keys(:force_reload, :with_deleted)
38
- forced_reload = options[:force_reload]
39
- with_deleted = options[: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(name, self.class, :with_deleted => with_deleted))
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 = {:dependent => :nullify}.update(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_#{to.name.singularize.underscore}_with_deleted", view_options))
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_#{to.name.singularize.underscore}", view_options))
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(klass_name)
10
- define_belongs_to_getter(klass_name)
11
- define_belongs_to_setter(klass_name)
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(klass_name, options = {})
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(klass_name, options)
21
- define_has_one_setter(klass_name, options)
22
- define_has_one_dependent_clearing(klass_name, options)
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(klass_name, options = {})
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(klass_name, options)
32
- define_has_many_setter_add(klass_name, options)
33
- define_has_many_setter_remove(klass_name, options)
34
- define_has_many_setter_remove_all(klass_name, options)
35
- define_has_many_dependent_clearing(klass_name, options)
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(klass_name)
39
- define_method klass_name.to_s do
40
- klass = self.class.get_class_from_name(klass_name)
41
- cached_version = instance_variable_get("@_cached_belongs_to_#{klass_name}")
42
- if cached_version.nil? and self["#{klass_name}_id"].present?
43
- cached_version = klass.find(self.send("#{klass_name}_id"), :auto_load => true)
44
- instance_variable_set("@_cached_belongs_to_#{klass_name}", cached_version)
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(klass_name)
51
- define_method "#{klass_name}=" do |val|
52
- klass = self.class.get_class_from_name(klass_name)
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("#{klass_name}_id=", val.id)
55
- instance_variable_set("@_cached_belongs_to_#{klass_name}", val)
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(klass_name, options)
60
- define_method klass_name.to_s do
61
- klass = self.class.get_class_from_name(klass_name)
62
- cached_version = instance_variable_get("@_cached_has_one_#{klass_name}")
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
- return cached_version
75
+ cached_version
65
76
  else
66
- cached_version = klass.send("find_by_#{self.class.foreign_key}".to_sym, self.id, {:auto_load => true})
67
- instance_variable_set("@_cached_has_one_#{klass_name}", cached_version)
68
- return cached_version
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(klass_name, options)
74
- define_method "#{klass_name}=" do |val|
75
- klass = self.class.get_class_from_name(klass_name)
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("#{klass_name}")
80
- old.send("#{self.class.foreign_key}=", nil) if old && options[:clear] == :nullify
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("#{self.class.foreign_key}=", self.id)
85
- instance_variable_set("@_cached_has_one_#{klass_name}", val)
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(klass_name, options)
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_#{klass_name}_after_destroy"
104
+ @_clear_dependents_after_delete_methods << "has_one_clear_#{name}_after_destroy"
93
105
 
94
106
  # define actual clearing/deleting
95
- define_method "has_one_clear_#{klass_name}_after_destroy" do
96
- klass = self.class.get_class_from_name(klass_name)
97
- dependent = klass.send("find_by_#{self.class.foreign_key}".to_sym, self.id)
98
- if options[:dependent] == :nullify
99
- dependent.send("#{self.class.foreign_key}=", nil) if dependent
100
- elsif options[:dependent] == :destroy
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(klass_name, options)
109
- define_method klass_name.to_s do
110
- klass = self.class.get_class_from_name(klass_name)
111
- cached_version = instance_variable_get("@_cached_has_many_#{klass_name}")
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
- return cached_version
128
+ cached_version
114
129
  else
115
- cached_version = klass.send("find_all_by_#{self.class.foreign_key}".to_sym, self.id, {:auto_load => true})
116
- instance_variable_set("@_cached_has_many_#{klass_name}", cached_version)
117
- return cached_version
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(klass_name, options)
123
- define_method "add_#{klass_name.to_s.singularize}" do |val|
124
- klass = self.class.get_class_from_name(klass_name)
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("#{self.class.foreign_key}=", self.id)
142
+ val.send("#{foreign_key_column}=", self.id)
127
143
  val.save(false)
128
- cached_version = instance_variable_get("@_cached_has_many_#{klass_name}") || []
129
- instance_variable_set("@_cached_has_many_#{klass_name}", cached_version << val)
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(klass_name, options)
134
- define_method "remove_#{klass_name.to_s.singularize}" do |val|
135
- klass = self.class.get_class_from_name(klass_name)
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(self.class.foreign_key.to_sym) == self.id
138
- if options[:clear] == :nullify
139
- val.send("#{self.class.foreign_key}=", nil)
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
- elsif options[:clear] == :destroy
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_#{klass_name}") || []
147
- instance_variable_set("@_cached_has_many_#{klass_name}", cached_version.delete_if{|x| x.id == val.id})
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(klass_name, options)
152
- define_method "remove_all_#{klass_name}" do
153
- klass = self.class.get_class_from_name(klass_name)
154
-
155
- all = klass.send("find_all_by_#{self.class.foreign_key}".to_sym, self.id)
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_#{klass_name.to_s.singularize}", item)
175
+ self.send("remove_#{name.to_s.singularize}", item)
159
176
  end
160
- instance_variable_set("@_cached_has_many_#{klass_name}", [])
177
+ instance_variable_set("@_cached_has_many_#{name}", [])
161
178
  end
162
179
  end
163
180
 
164
- def define_has_many_dependent_clearing(klass_name, options)
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_#{klass_name}_after_destroy"
184
+ @_clear_dependents_after_delete_methods << "has_many_clear_#{name}_after_destroy"
168
185
 
169
186
  # define actual clearing/deleting
170
- define_method "has_many_clear_#{klass_name}_after_destroy" do
171
- klass = self.class.get_class_from_name(klass_name)
172
- dependents = klass.send("find_all_by_#{self.class.foreign_key}".to_sym, self.id)
173
- if options[:dependent] == :nullify
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("#{self.class.foreign_key}=", nil)
194
+ dependent.send("#{foreign_key_column}=", nil)
176
195
  end
177
- elsif options[:dependent] == :destroy
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('__','/').gsub('__','::').classify.constantize
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.3'
7
+ VERSION = '0.3.4'
8
8
  class Error < RuntimeError; end
9
9
  class RecordNotFound < RuntimeError; end
10
10
  end
@@ -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
- - 3
9
- version: 0.3.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-05-19 00:00:00 +02:00
17
+ date: 2010-06-17 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency