simply_couch 0.1.0

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +182 -0
  3. data/LICENSE.txt +15 -0
  4. data/README.md +294 -0
  5. data/lib/core_ext/date.rb +15 -0
  6. data/lib/core_ext/time.rb +23 -0
  7. data/lib/simply_couch/class_methods_base.rb +72 -0
  8. data/lib/simply_couch/has_attachment.rb +225 -0
  9. data/lib/simply_couch/include_relation.rb +160 -0
  10. data/lib/simply_couch/instance_methods.rb +356 -0
  11. data/lib/simply_couch/locale/en.yml +5 -0
  12. data/lib/simply_couch/model/ancestry.rb +307 -0
  13. data/lib/simply_couch/model/association_property.rb +26 -0
  14. data/lib/simply_couch/model/attachments.rb +90 -0
  15. data/lib/simply_couch/model/belongs_to.rb +140 -0
  16. data/lib/simply_couch/model/database.rb +209 -0
  17. data/lib/simply_couch/model/embedded_in.rb +196 -0
  18. data/lib/simply_couch/model/find_by.rb +202 -0
  19. data/lib/simply_couch/model/finders.rb +77 -0
  20. data/lib/simply_couch/model/has_and_belongs_to_many.rb +223 -0
  21. data/lib/simply_couch/model/has_many.rb +177 -0
  22. data/lib/simply_couch/model/has_many_embedded.rb +187 -0
  23. data/lib/simply_couch/model/has_one.rb +75 -0
  24. data/lib/simply_couch/model/pagination.rb +25 -0
  25. data/lib/simply_couch/model/pagination_options.rb +55 -0
  26. data/lib/simply_couch/model/persistence.rb +411 -0
  27. data/lib/simply_couch/model/properties.rb +11 -0
  28. data/lib/simply_couch/model/validations.rb +28 -0
  29. data/lib/simply_couch/model/view/base_view_spec.rb +115 -0
  30. data/lib/simply_couch/model/view/custom_view_spec.rb +49 -0
  31. data/lib/simply_couch/model/view/custom_views.rb +50 -0
  32. data/lib/simply_couch/model/view/lists.rb +25 -0
  33. data/lib/simply_couch/model/view/model_view_spec.rb +106 -0
  34. data/lib/simply_couch/model/view/properties_view_spec.rb +53 -0
  35. data/lib/simply_couch/model/view/raw_view_spec.rb +30 -0
  36. data/lib/simply_couch/model/view/view_query.rb +98 -0
  37. data/lib/simply_couch/model/view.rb +8 -0
  38. data/lib/simply_couch/model/views/array_property_view_spec.rb +26 -0
  39. data/lib/simply_couch/model/views/deleted_model_view_spec.rb +43 -0
  40. data/lib/simply_couch/model/views.rb +2 -0
  41. data/lib/simply_couch/model.rb +195 -0
  42. data/lib/simply_couch/rake.rb +23 -0
  43. data/lib/simply_couch/storage.rb +147 -0
  44. data/lib/simply_couch.rb +26 -0
  45. metadata +144 -0
@@ -0,0 +1,187 @@
1
+ module SimplyCouch
2
+ module Model
3
+ module HasManyEmbedded
4
+ def has_many_embedded(name, options = {})
5
+ check_existing_properties(name, SimplyCouch::Model::HasMany::Property)
6
+ properties << SimplyCouch::Model::HasManyEmbedded::Property.new(self, name, options)
7
+ end
8
+
9
+ def define_has_many_embedded_getter(name, options)
10
+ # Make an alias to the property getter.
11
+ property_getter_method = "#{name}_property_getter"
12
+ alias_method property_getter_method, name
13
+ define_method(name) do |*args|
14
+ current = instance_variable_get("@#{name}")
15
+ return current if current && current.respond_to?(:parent_object_set?)
16
+ # Rebuild current, ensure parent object
17
+ current = Array.wrap(self.send(property_getter_method)).map do |h|
18
+ if h.is_a?(options[:class_name].constantize)
19
+ o = h
20
+ o.parent_object = self
21
+ else
22
+ o = options[:class_name].constantize.new
23
+ o._document = h
24
+ o.parent_object = self
25
+ end
26
+ o
27
+ end
28
+ def current.parent_object_set?
29
+ true
30
+ end
31
+ instance_variable_set("@#{name}", current)
32
+ return current
33
+ local_options = args.first && args.first.is_a?(Hash)
34
+ forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
35
+
36
+ cached_results = send("_get_cached_#{name}") || {}
37
+ cache_key = _cache_key_for(local_options)
38
+ debugger
39
+ if forced_reload || cached_results[cache_key].nil?
40
+ #cached_results[cache_key] = get_embedded(options[:class_name], self.class, :with_deleted => with_deleted, :limit => limit, :descending => descending, :foreign_key => options[:foreign_key])
41
+ cached_results[cache_key] = Array.wrap(self.send(property_getter_method)).map{|h| o = options[:class_name].constantize.new; o._document = h; o}.map{|o| o.parent_object = self; o}
42
+ instance_variable_set("@#{name}", cached_results)
43
+ self.class.set_parent_has_many_embedded_association_object(self, cached_results[cache_key])
44
+ end
45
+ cached_results[cache_key]
46
+ end
47
+ end
48
+
49
+ def define_has_many_embedded_setter(name, options)
50
+ define_method("#{name}=") do |values|
51
+ klass = self.class.get_class_from_name(name)
52
+ raise ArgumentError, "expected Array got #{values.class}" unless values.is_a?(Array)
53
+ instance_variable_set("@#{name}", []) unless instance_variable_get("@#{name}")
54
+ iid = 0
55
+ for value in values
56
+ if value.is_a?(Hash)
57
+ newval = klass.new
58
+ newval._document = value
59
+ newval.updated_at ||= Time.now
60
+ newval.created_at ||= Time.now
61
+ else
62
+ newval = value
63
+ end
64
+ newval.index = iid
65
+ instance_variable_get("@#{name}") << newval
66
+ iid += 1
67
+ end
68
+ save
69
+ end
70
+ end
71
+
72
+ def define_has_many_embedded_setter_add(name, options)
73
+ define_method("add_#{name.to_s.singularize}") do |value|
74
+ klass = self.class.get_class_from_name(name)
75
+ raise ArgumentError, "expected #{klass} got #{value.class}" unless value.is_a?(klass)
76
+ if value.is_a?(Hash)
77
+ newval = klass.new
78
+ newval._document = value
79
+ newval.updated_at ||= Time.now
80
+ newval.created_at ||= Time.now
81
+ else
82
+ newval = value
83
+ end
84
+ newval.index = (instance_variable_get("@#{name}") || []).size
85
+ instance_variable_get("@#{name}") << newval
86
+ save
87
+ end
88
+ end
89
+
90
+ def define_has_many_embedded_setter_remove(name, options)
91
+ define_method "remove_#{name.to_s.singularize}" do |value|
92
+ klass = self.class.get_class_from_name(name)
93
+ if value.is_a?(klass)
94
+ found = instance_variable_get("@#{name}").delete(value)
95
+ if found
96
+ self.is_dirty
97
+ self.send("reset_#{name}_index_values")
98
+ end
99
+ else
100
+ raise ArgumentError, "expected #{klass} got #{value.class}"
101
+ end
102
+ return save
103
+ end
104
+ end
105
+
106
+
107
+ def define_reset_index_values(name, options)
108
+ define_method "reset_#{name}_index_values" do
109
+ i = 0
110
+ for embedded_document in (instance_variable_get("@#{name}") || [])
111
+ embedded_document.index = i
112
+ i += 1
113
+ end
114
+ end
115
+ end
116
+
117
+ # Not converted yet
118
+ def define_has_many_embedded_setter_remove_all(name, options)
119
+ define_method "remove_all_#{name}" do
120
+ all = send("#{name}", :force_reload => true)
121
+
122
+ all.collect{|i| i}.each do |item|
123
+ send("remove_#{name.to_s.singularize}", item)
124
+ end
125
+ end
126
+ end
127
+
128
+ # Not converted yet
129
+ def define_has_many_embedded_count(name, options, through = nil)
130
+ method_name = name.to_s.singularize.underscore.gsub('/', '__') + "_count"
131
+ define_method(method_name) do |*args|
132
+ local_options = args.first && args.first.is_a?(Hash) && args.first
133
+ if local_options
134
+ local_options.assert_valid_keys(:force_reload, :with_deleted)
135
+ forced_reload = local_options[:force_reload]
136
+ with_deleted = local_options[:with_deleted]
137
+ else
138
+ forced_reload = false
139
+ with_deleted = false
140
+ end
141
+
142
+ if forced_reload || instance_variable_get("@#{method_name}").nil?
143
+ instance_variable_set("@#{method_name}", count_associated(through || options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key]))
144
+ end
145
+ instance_variable_get("@#{method_name}")
146
+ end
147
+ end
148
+
149
+ # Not converted yet
150
+ def set_parent_has_many_embedded_association_object(parent, child_collection)
151
+ child_collection.each do |child|
152
+ if child.respond_to?("#{parent.class.name.to_s.singularize.downcase}=")
153
+ child.send("#{parent.class.name.to_s.singularize.camelize.downcase}=", parent)
154
+ end
155
+ end
156
+ end
157
+
158
+ class Property < SimplyCouch::Model::AssociationProperty
159
+
160
+ def initialize(owner_clazz, name, options = {})
161
+ options = {
162
+ :dependent => :nullify,
163
+ :through => nil,
164
+ :class_name => name.to_s.singularize.camelize,
165
+ :foreign_key => nil
166
+ }.update(options)
167
+ @name, @options = name, options
168
+
169
+ options.assert_valid_keys(:dependent, :through, :class_name, :foreign_key)
170
+
171
+ owner_clazz.class_eval do
172
+ property name, :type => Array
173
+ _define_cache_accessors(name, options)
174
+ define_has_many_embedded_getter(name, options)
175
+ define_has_many_embedded_setter(name, options)
176
+ define_has_many_embedded_setter_add(name, options)
177
+ define_has_many_embedded_setter_remove(name, options)
178
+ define_has_many_embedded_setter_remove_all(name, options)
179
+ define_reset_index_values(name, options)
180
+ define_has_many_embedded_count(name, options)
181
+ end
182
+ end
183
+
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,75 @@
1
+ module SimplyCouch
2
+ module Model
3
+ module HasOne
4
+ def has_one(name, options = {})
5
+ check_existing_properties(name, SimplyCouch::Model::HasOne::Property)
6
+ properties << SimplyCouch::Model::HasOne::Property.new(self, name, options)
7
+ end
8
+
9
+ def define_has_one_setter(name, options)
10
+ define_method("#{name}=") do |value|
11
+ klass = self.class.get_class_from_name(name)
12
+ raise ArgumentError, "expected #{klass} got #{value.class}" unless value.nil? || value.is_a?(klass)
13
+ old_value = send("#{name}", :force_reload => true)
14
+ if value.nil?
15
+ instance_variable_set("@#{name}", nil)
16
+ else
17
+ instance_variable_set("@#{name}", value)
18
+ value.send("#{self.class.foreign_key}=", id)
19
+ value.save
20
+ end
21
+
22
+ if old_value
23
+ if options[:dependent] == :destroy
24
+ old_value.destroy
25
+ else
26
+ old_value.send("#{self.class.foreign_property}=", nil)
27
+ old_value.save
28
+ end
29
+ end
30
+ old_value
31
+ end
32
+ end
33
+
34
+ def define_has_one_getter(name, options)
35
+ define_method(name) do |*args|
36
+ local_options = args.first && args.first.is_a?(Hash) && args.first
37
+ forced_reload, with_deleted, limit, descending = extract_association_options(local_options)
38
+
39
+ if forced_reload || instance_variable_get("@#{name}").nil?
40
+ found_object = find_one_associated(options[:class_name], self.class, :with_deleted => with_deleted, :foreign_key => options[:foreign_key])
41
+ instance_variable_set("@#{name}", found_object)
42
+ self.class.set_parent_has_one_association_object(self, found_object)
43
+ end
44
+ instance_variable_get("@#{name}")
45
+ end
46
+ end
47
+
48
+ def set_parent_has_one_association_object(parent, child)
49
+ if child.respond_to?("#{parent.class.name.to_s.singularize.downcase}=")
50
+ child.send("#{parent.class.name.to_s.singularize.camelize.downcase}=", parent)
51
+ end
52
+ end
53
+
54
+ class Property < SimplyCouch::Model::AssociationProperty
55
+
56
+ def initialize(owner_clazz, name, options = {})
57
+ options = {
58
+ :dependent => :nullify,
59
+ :class_name => name.to_s.singularize.camelize,
60
+ :foreign_key => nil
61
+ }.update(options)
62
+ @name, @options = name, options
63
+
64
+ options.assert_valid_keys(:dependent, :class_name, :foreign_key)
65
+
66
+ owner_clazz.class_eval do
67
+ define_has_one_getter(name, options)
68
+ define_has_one_setter(name, options)
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,25 @@
1
+ module SimplyCouch
2
+ module Model
3
+ module Pagination
4
+ def total_pages_method(setter = nil)
5
+ @_total_pages_method = setter if setter
6
+ @_total_pages_method || 'total_pages'
7
+ end
8
+
9
+ def current_page_method(setter = nil)
10
+ @_current_page_method = setter if setter
11
+ @_current_page_method || 'current_page'
12
+ end
13
+
14
+ def num_pages_method(setter = nil)
15
+ @_num_pages_method = setter if setter
16
+ @_num_pages_method || 'num_pages'
17
+ end
18
+
19
+ def per_page_method(setter = nil)
20
+ @_per_page_method = setter if setter
21
+ @_per_page_method || 'per_page'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,55 @@
1
+ module SimplyCouch
2
+ module Model
3
+ module PaginationOptions
4
+ def with_pagination_options(options, &block)
5
+ # Allow for manual setting of total_entries
6
+ # If none given, default fallback will be total_rows
7
+ # This is not true for a paginated find_all_by construction
8
+ total_entries = options.delete(:total_entries)
9
+ # Add limit and skip to options if requested
10
+ if ([:page, :per_page] & options.keys).any? # Pagination active
11
+ page = [options.delete(:page).to_i, 1].max
12
+ per_page = options.delete(:per_page).to_i # Can be string
13
+ per_page = 22 unless per_page > 0 # Nill will be 0
14
+ options[:limit] = per_page
15
+ options[:skip] = (page - 1) * options[:limit]
16
+ elsif options[:offset] && options[:limit] # Sql syntax support
17
+ options[:skip] = options.delete(:offset)
18
+ options.delete(:order)
19
+ page = (options[:skip].to_i / options[:limit].to_i).floor.succ
20
+ per_page = options[:limit]
21
+ else
22
+ page = 1
23
+ per_page = 22
24
+ end
25
+ result = block.call(options)
26
+ if result
27
+ result.instance_eval <<-PAGINATION, __FILE__, __LINE__
28
+ unless respond_to?(:total_rows)
29
+ def total_rows
30
+ 1
31
+ end
32
+ end
33
+ def total_entries
34
+ #{total_entries || 'total_rows'}
35
+ end
36
+ alias total_count total_entries
37
+
38
+ def #{current_page_method}
39
+ #{page}
40
+ end
41
+ def #{num_pages_method}
42
+ return 1 if total_entries.zero?
43
+ (total_entries.to_f / #{per_page}).ceil
44
+ end
45
+ alias :#{total_pages_method} :#{num_pages_method}
46
+ def #{per_page_method}
47
+ #{per_page}
48
+ end
49
+ PAGINATION
50
+ end
51
+ result
52
+ end
53
+ end
54
+ end
55
+ end