-
Notifications
You must be signed in to change notification settings - Fork 116
/
design.rb
176 lines (141 loc) · 4.77 KB
/
design.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
module CouchRest
module Model
module Designs
class Design < ::CouchRest::Design
# The model Class that this design belongs to and method name
attr_accessor :model, :method_name
# Can this design save itself to the database?
# If false, the design will be loaded automatically before a view is executed.
attr_accessor :auto_update
# Instantiate a new design document for this model
def initialize(model, prefix = nil)
self.model = model
self.method_name = self.class.method_name(prefix)
suffix = prefix ? "_#{prefix}" : ''
self["_id"] = "_design/#{model.to_s}#{suffix}"
apply_defaults
end
def sync(db = nil)
if auto_update
db ||= database
# do we need to continue?
return self if cache_checksum(db) == checksum
# Load up the last copy. We never overwrite the remote copy
# as it may contain views that are not used or known about by
# our model.
doc = load_from_database(db)
if !doc || doc['couchrest-hash'] != checksum
# We need to save something
if doc
# Different! Update.
doc.merge!(to_hash)
else
# No previous doc, use our version.
doc = self
end
db.save_doc(doc)
end
set_cache_checksum(db, checksum)
end
self
end
def checksum
sum = self['couchrest-hash']
if sum && (@_original_hash == to_hash)
sum
else
checksum!
end
end
def database
model.database
end
# Override the default #uri method for one that accepts
# the current database.
# This is used by the caching code.
def uri(db = database)
"#{db.root}/#{self['_id']}"
end
######## VIEW HANDLING ########
# Create a new view object.
# This overrides the normal CouchRest Design view method
def view(name, opts = {})
CouchRest::Model::Designs::View.new(self, model, opts, name)
end
# Helper method to provide a list of all the views
def view_names
self['views'].keys
end
def has_view?(name)
view_names.include?(name.to_s)
end
# Add the specified view to the design doc the definition was made in
# and create quick access methods in the model.
def create_view(name, opts = {})
View.define_and_create(self, name, opts)
end
######## FILTER HANDLING ########
def create_filter(name, function)
filters = (self['filters'] ||= {})
filters[name.to_s] = function
end
protected
def load_from_database(db = database)
db.get(self['_id'])
rescue RestClient::ResourceNotFound
nil
end
# Calculate and update the checksum of the Design document.
# Used for ensuring the latest version has been sent to the database.
#
# This will generate an flatterned, ordered array of all the elements of the
# design document, convert to string then generate an MD5 Hash. This should
# result in a consisitent Hash accross all platforms.
#
def checksum!
# Get a deep copy of hash to compare with
@_original_hash = Marshal.load(Marshal.dump(to_hash))
# create a copy of basic elements
base = self.dup
base.delete('_id')
base.delete('_rev')
base.delete('couchrest-hash')
result = nil
flatten =
lambda {|r|
(recurse = lambda {|v|
if v.is_a?(Hash) || v.is_a?(CouchRest::Document)
v.to_a.map{|v| recurse.call(v)}.flatten
elsif v.is_a?(Array)
v.flatten.map{|v| recurse.call(v)}
else
v.to_s
end
}).call(r)
}
self['couchrest-hash'] = Digest::MD5.hexdigest(flatten.call(base).sort.join(''))
end
def cache
Thread.current[:couchrest_design_cache] ||= {}
end
def cache_checksum(db)
cache[uri(db)]
end
def set_cache_checksum(db, checksum)
cache[uri(db)] = checksum
end
def apply_defaults
merge!(
"language" => "javascript",
"views" => { }
)
end
class << self
def method_name(prefix = nil)
(prefix ? "#{prefix}_" : '') + 'design_doc'
end
end
end
end
end
end