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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +182 -0
- data/LICENSE.txt +15 -0
- data/README.md +294 -0
- data/lib/core_ext/date.rb +15 -0
- data/lib/core_ext/time.rb +23 -0
- data/lib/simply_couch/class_methods_base.rb +72 -0
- data/lib/simply_couch/has_attachment.rb +225 -0
- data/lib/simply_couch/include_relation.rb +160 -0
- data/lib/simply_couch/instance_methods.rb +356 -0
- data/lib/simply_couch/locale/en.yml +5 -0
- data/lib/simply_couch/model/ancestry.rb +307 -0
- data/lib/simply_couch/model/association_property.rb +26 -0
- data/lib/simply_couch/model/attachments.rb +90 -0
- data/lib/simply_couch/model/belongs_to.rb +140 -0
- data/lib/simply_couch/model/database.rb +209 -0
- data/lib/simply_couch/model/embedded_in.rb +196 -0
- data/lib/simply_couch/model/find_by.rb +202 -0
- data/lib/simply_couch/model/finders.rb +77 -0
- data/lib/simply_couch/model/has_and_belongs_to_many.rb +223 -0
- data/lib/simply_couch/model/has_many.rb +177 -0
- data/lib/simply_couch/model/has_many_embedded.rb +187 -0
- data/lib/simply_couch/model/has_one.rb +75 -0
- data/lib/simply_couch/model/pagination.rb +25 -0
- data/lib/simply_couch/model/pagination_options.rb +55 -0
- data/lib/simply_couch/model/persistence.rb +411 -0
- data/lib/simply_couch/model/properties.rb +11 -0
- data/lib/simply_couch/model/validations.rb +28 -0
- data/lib/simply_couch/model/view/base_view_spec.rb +115 -0
- data/lib/simply_couch/model/view/custom_view_spec.rb +49 -0
- data/lib/simply_couch/model/view/custom_views.rb +50 -0
- data/lib/simply_couch/model/view/lists.rb +25 -0
- data/lib/simply_couch/model/view/model_view_spec.rb +106 -0
- data/lib/simply_couch/model/view/properties_view_spec.rb +53 -0
- data/lib/simply_couch/model/view/raw_view_spec.rb +30 -0
- data/lib/simply_couch/model/view/view_query.rb +98 -0
- data/lib/simply_couch/model/view.rb +8 -0
- data/lib/simply_couch/model/views/array_property_view_spec.rb +26 -0
- data/lib/simply_couch/model/views/deleted_model_view_spec.rb +43 -0
- data/lib/simply_couch/model/views.rb +2 -0
- data/lib/simply_couch/model.rb +195 -0
- data/lib/simply_couch/rake.rb +23 -0
- data/lib/simply_couch/storage.rb +147 -0
- data/lib/simply_couch.rb +26 -0
- 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
|