couch_cloner 0.0.1

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/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "couch_cloner"
3
+ s.version = File.read "VERSION"
4
+ s.authors = "Matt Parker"
5
+ s.homepage = "http://github.com/moonmaster9000/couch_cloner"
6
+ s.summary = "Clone and schedule CouchDB documents"
7
+ s.description = "Create clones of CouchDB documents, and schedule them for publication."
8
+ s.email = "moonmaster9000@gmail.com"
9
+ s.files = Dir["lib/**/*"] << "VERSION" << "readme.markdown" << "couch_cloner.gemspec"
10
+ s.test_files = Dir["feature/**/*"]
11
+
12
+ s.add_development_dependency "cucumber"
13
+ s.add_development_dependency "rspec"
14
+ s.add_development_dependency "couchrest_model_config"
15
+ s.add_development_dependency "couch_publish", "~> 0.0.3"
16
+ s.add_development_dependency "couch_visible"
17
+ s.add_development_dependency "timecop"
18
+
19
+ s.add_dependency "couchrest", "1.0.1"
20
+ s.add_dependency "couchrest_model", "~> 1.0.0"
21
+ s.add_dependency "recloner", "~> 0.1.1"
22
+ s.add_dependency "couch_view", "~> 0.0.3"
23
+ end
@@ -0,0 +1,7 @@
1
+ require 'couchrest_model'
2
+ require 'couch_view'
3
+ require 'couch_cloner/couch_cloner'
4
+ require 'couch_cloner/clone'
5
+ require 'couch_cloner/scheduling'
6
+ require 'couch_cloner/query'
7
+ require 'couch_cloner/maps/by_clone_id_and_start_time'
@@ -0,0 +1,42 @@
1
+ module CouchCloner
2
+ module Clone
3
+ def self.included(base)
4
+ base.property :clone_id
5
+ base.send :include, InstanceMethods
6
+ end
7
+
8
+ module InstanceMethods
9
+ def clone(&block)
10
+ verify_clone_preconditions
11
+ block ||= Proc.new {}
12
+
13
+ property_names = properties.map(&:name) - (protected_properties.map(&:name) + %w{_id _attachments _rev milestone_memories})
14
+ attrs = property_names.inject({}){|hash, x|
15
+ val = send(x)
16
+ val = val.to_a if val.class == CouchRest::Model::CastedArray
17
+ hash[x] = val
18
+ hash
19
+ }
20
+
21
+ self.class.new(attrs).tap(&block)
22
+ end
23
+
24
+ def clone!(&block)
25
+ verify_clone_preconditions
26
+ has_block = !block.nil?
27
+ block ||= Proc.new {}
28
+ next_id = database.server.next_uuid
29
+ copy next_id
30
+ doc = self.class.get(next_id)
31
+ has_block ? doc.tap(&block).tap {|d| d.save} : doc
32
+ end
33
+
34
+ private
35
+ def verify_clone_preconditions
36
+ unless self.clone_id
37
+ raise "You must specify a non-nil clone_id on your '#{self.class}' instance before you can clone it."
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ module CouchCloner
2
+ def self.included(base)
3
+ base.send :include, CouchView unless base.ancestors.include?(CouchView)
4
+ base.send :include, CouchCloner::Clone
5
+ base.send :include, CouchCloner::Scheduling
6
+ base.send :include, CouchCloner::Query
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module CouchCloner
2
+ class ByCloneIdAndStartTime
3
+ include CouchView::Map
4
+
5
+ def map
6
+ "
7
+ function(doc){
8
+ if (#{conditions} && (doc.start == null || doc.start == '')) {
9
+ emit([doc.clone_id, {'created_at': doc.created_at}], null)
10
+ } else if (#{conditions} && doc.start != null && doc.start != ''){
11
+ emit([doc.clone_id, doc.start], null)
12
+ }
13
+ }
14
+ "
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,77 @@
1
+ module CouchCloner
2
+ module Query
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ base.map :clone_id
6
+ base.couch_view :by_clone_id_and_start_time do
7
+ map CouchCloner::ByCloneIdAndStartTime
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def map_by_clone_id_and_start(clone_id, start=nil)
13
+ map_by_clone_id_and_start_time.startkey!([clone_id, start]).endkey!([clone_id, {:end => nil}])
14
+ end
15
+
16
+ def count_by_clone_id_and_start(clone_id, start=nil)
17
+ count_by_clone_id_and_start_time.
18
+ startkey!([clone_id, start]).
19
+ endkey!([clone_id, {:end => nil}])
20
+ end
21
+
22
+ def map_active_by_clone_id(clone_id)
23
+ map_by_clone_id_and_start_time.
24
+ startkey!([clone_id, Time.now]).
25
+ endkey!([clone_id]).
26
+ descending!(true)
27
+ end
28
+
29
+ def map_future_by_clone_id(clone_id)
30
+ map_by_clone_id_and_start_time.
31
+ startkey!([clone_id, Time.now]).
32
+ endkey!([clone_id, {:end => nil}])
33
+ end
34
+
35
+ #new
36
+ def count_future_by_clone_id(clone_id)
37
+ count_by_clone_id_and_start_time.
38
+ startkey!([clone_id, Time.now]).
39
+ endkey!([clone_id, {:end => nil}])
40
+ end
41
+
42
+ def map_past_by_clone_id(clone_id)
43
+ map_by_clone_id_and_start_time.
44
+ startkey!([clone_id, Time.now]).
45
+ endkey!([clone_id]).
46
+ descending!(true)
47
+ end
48
+
49
+ def count_past_by_clone_id(clone_id)
50
+ count_by_clone_id_and_start_time.
51
+ startkey!([clone_id, Time.now]).
52
+ endkey!([clone_id]).
53
+ descending!(true)
54
+ end
55
+
56
+ def map_clone_ids
57
+ map_by_clone_id.
58
+ reduce!(true).
59
+ group!(true)
60
+ end
61
+
62
+ def count_clone_ids!
63
+ map_by_clone_id.
64
+ reduce!(true).
65
+ group!(true).get!['rows'].count
66
+ end
67
+
68
+ def map_last_future_by_clone_id(clone_id)
69
+ map_by_clone_id_and_start_time.
70
+ startkey!([clone_id, {:end => nil}]).
71
+ endkey!([clone_id]).
72
+ descending!(true).
73
+ limit!(1)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,15 @@
1
+ module CouchCloner
2
+ module Scheduling
3
+ def self.included(base)
4
+ base.property :start, Time
5
+ base.validate :uniqueness_of_start_and_clone_id
6
+ end
7
+
8
+ private
9
+ def uniqueness_of_start_and_clone_id
10
+ if !start.nil? && self.class.count_by_clone_id_and_start_time.key([clone_id, start]).get! != 0
11
+ errors.add :start, "must be unique for the clone_id group '#{clone_id}'"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,293 @@
1
+ ## CouchCloner
2
+
3
+ Clone your CouchDB `CouchRest::Model::Base` documents and schedule them for publishing.
4
+
5
+
6
+ ## Installation
7
+
8
+ It's a ruby gem called `couch_cloner`. Install it.
9
+
10
+
11
+ ## Setup
12
+
13
+ Simply include the module `CouchCloner` into your `CouchRest::Model::Base` document. For example:
14
+
15
+ class HtmlSnippet < CouchRest::Model::Base
16
+ include CouchCloner
17
+ end
18
+
19
+ Setup complete.
20
+
21
+
22
+ ## Cloning (.clone/.clone!)
23
+
24
+ Let's imagine we'd like to create an HtmlSnippet that appears on the home page of our website. Our model definition might look like this:
25
+
26
+ class HtmlSnippet < CouchRest::Model::Base
27
+ include CouchCloner
28
+
29
+ property :content
30
+
31
+ timestamps!
32
+ end
33
+
34
+ And our snippet might look like this:
35
+
36
+ homepage = HtmlSnippet.create :clone_id => "homepage", :content => "<h1>Homepage!</h1>"
37
+
38
+ OK, so that's a pretty lame bit of content, but did you notice the `clone_id`? If you're going to clone, you have to set a `clone_id` on your document first. The `clone_id` is the shared identifier between all of your clones. That's important; you'll see why in a few moments. Read on.
39
+
40
+ So now your content administrators want to use your CMS to schedule clones of this content to publish on the site several days in advance. How do we clone it?
41
+
42
+ We can create a soft clone (i.e., a new HtmlSnippet based on the original, but not yet persisted to the database) via the `clone` method:
43
+
44
+ next = homepage.clone
45
+
46
+ next.content #==> "<h1>Homepage!</h1>"
47
+ next.clone_id #==> "homepage"
48
+ next.new_record? #==> true
49
+
50
+ We can create a persisted clone with the `clone!` method:
51
+
52
+ next = homepage.clone!
53
+
54
+ next.content #==> "<h1>Homepage!</h1>"
55
+ next.clone_id #==> "homepage"
56
+ next.new_record? #==> false
57
+
58
+ Note that, when cloned, the `start` scheduling property of the clone is not copied. See the next section for details about how scheduling works.
59
+
60
+
61
+ ## Scheduling (.start)
62
+
63
+ The utility of these clones is most apparent when you schedule multiple clones. The scheduling has only one constraint: each document in a clone group must have a unique date/time stamp, or `nil`.
64
+
65
+ Returning to our previous example of `next` and `homepage` HtmlSnippet clones:
66
+
67
+ homepage.start = Time.now.beginning_of_day
68
+ homepage.save #==> true
69
+
70
+ We've now scheduled the `original` clone to start at the beginning of today.
71
+
72
+ Next, we'll try to schedule the `next` clone for the same time:
73
+
74
+ next.start = Time.now.beginning_of_day
75
+ next.save #==> false
76
+ next.errors[:start] #==> "must be unique"
77
+
78
+ Since we need to create a unique timestamp, we'll schedule `next` to start tomorrow:
79
+
80
+ next.start = 1.day.from_now
81
+ next.save #==> true
82
+ next.errors.empty? #==> true
83
+
84
+ We could proceed creating and scheduling clones like this ad infinitum.
85
+
86
+
87
+ ## Retrieving all the clones in a clone_id group (.map_by_clone_id/.count_by_clone_id)
88
+
89
+ To retrieve all of the clones with the same clone_id, call the `.map_by_clone_id` query proxy and provide it with a clone_id key that you want to look up:
90
+
91
+ HtmlSnippet.map_by_clone_id.key("some_clone_id").get!
92
+
93
+ If you're unfamiliar with this syntax, read up on the `couch_view` gem at http://github.com/moonmaster9000/couch_view
94
+
95
+ This will return all clones with that clone_id. You can pass all of the usual map/reduce options to this method (e.g., limit/skip):
96
+
97
+ HtmlSnippet.map_by_clone_id.key("some_clone_id").limit(10).skip(10).get!
98
+
99
+ Clones with the same clone_id will be sorted by their `_id` (this is simply how CouchDB works).
100
+
101
+ A more useful sorting option is to have the documents sorted by their `start` property. Clones with a `start` of `nil` or `""` will sort last, and will order by their `created_at` property. Thus, it's as if the clones with a `start` time are assumed to be scheduled at time `infinity + created_at`. Pretty cool, right?
102
+
103
+ To get all the clones with the same `clone_id` sorted in this order, call `map_by_clone_id_and_start`:
104
+
105
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id")
106
+
107
+ This will return a query proxy with a startkey of `["some_clone_id"]`, and an endkey of `["some_clone_id", {:end => true}]`, which will select all of the documents with clone id "some_clone_id", sorted by their "start" time.
108
+
109
+ You can pass off all of the usual map/reduce options to this view:
110
+
111
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id").limit(10).skip(1).get!
112
+
113
+ If you'd like to retrieve only a subset of this view, you can use the `:key` map/reduce option. For example, suppose we'd like to see all clones scheduled to start after now:
114
+
115
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id", Time.now)
116
+
117
+ The `endkey` will still be automatically defaulted to `["some_clone_id", {:end => nil}]`
118
+
119
+ Lastly, you can find the total number of clones with the same `clone_id` by calling the `count_by_clone_id` class method on your model:
120
+
121
+ HtmlSnippet.count_by_clone_id("some_clone_id")
122
+
123
+ If you wanted to count only a subset of your clones based on their `start` time, you can use `count_by_clone_id_and_start`:
124
+
125
+ HtmlSnippet.count_by_clone_id_and_start("some_clone_id", Time.now)
126
+
127
+ Again, the `endkey` will be automatically defaulted to `["some_clone_id", {:end => nil}]`
128
+
129
+
130
+ ## Retrieving the active clone by clone_id (.map_active_by_clone_id)
131
+
132
+ Now that we've created an original clone scheduled today, and a `next` clone scheduled tomorrow, let's determine which one is currently active:
133
+
134
+ HtmlSnippet.map_active_by_clone_id("homepage").get!.first.content #==> "<h1>this is awesome</h1>"
135
+
136
+ The `map_active_by_clone_id` method accepts a `clone_id` (in our case a `label`), and returns a query proxy that will return either zero or one results (zero if no currently active `HtmlSnippet` is found with that label, otherwise, one for the currently active `HtmlSnippet`).
137
+
138
+
139
+ ## Retrieving clones scheduled into the future (.map_future_by_clone_id)
140
+
141
+ We can get a list of the future clones by label via the `map_future_by_clone_id` method:
142
+
143
+ past = HtmlSnippet.create :clone_id => "homepage", :start => 1.day.ago
144
+ next = HtmlSnippet.create :clone_id => "homepage", :start => 1.day.from_now
145
+
146
+ HtmlSnippet.map_future_by_clone_id("homepage").get!
147
+ #==> returns the homepage snippet "next" that starts one day from now
148
+
149
+ This is essentially a shortcut for:
150
+
151
+ HtmlSnippet.map_by_clone_id_and_start("homepage", Time.now)
152
+
153
+ If we create a clone with a `start` of `nil`, they will show up sorted at the end of `map_future_by_clone_id`:
154
+
155
+ future = next.clone! #==> remember, on clone, the `start` property is not copied
156
+ future.start.should == nil
157
+ future.save
158
+
159
+ HtmlSnippet.map_future_by_clone_id("homepage").get!
160
+ #==> would return an array consisting of the `next` clone followed by the `future` clone
161
+
162
+ If there are multiple clones with a start of `nil`, they will sort by their `created_at` timestamp.
163
+
164
+ We also provide a method for counting the number of active and future clones in a given clone_id group:
165
+
166
+ HtmlSnippet.count_future_by_clone_id("some_clone_id").get!
167
+
168
+
169
+ ## Retrieving past clones (.map_past_by_clone_id)
170
+
171
+ You might find it useful to retrieve only the clones scheduled in the past. You can use the `map_past_by_clone_id` method:
172
+
173
+ HtmlSnippet.map_past_by_clone_id("some_clone_id").get!
174
+
175
+ They will be ordered by their start date.
176
+
177
+ You can also count the number of past clones via the `count_past_by_clone_id`:
178
+
179
+ HtmlSnippet.count_past_by_clone_id("some_clone_id").get!
180
+
181
+
182
+ ## Retreiving the list of currently used clone_ids (.map_clone_ids)
183
+
184
+ You can retrieve an array of all of the `clone_id`'s in use by calling the `map_clone_ids` method on your model:
185
+
186
+ HtmlSnippet.database.recreate!
187
+ HtmlSnippet.create :clone_id => "homepage"
188
+ HtmlSnippet.create :clone_id => "contact_us"
189
+ HtmlSnippet.create :clone_id => "news"
190
+
191
+ HtmlSnippet.map_clone_ids.get!['rows'].map {|row| row['key']}
192
+ #==> ["contact_us", "homepage", "news"]
193
+
194
+ You can use all of the map/reduce options you're used to (e.g., limit/skip):
195
+
196
+ HtmlSnippet.map_clone_ids.limit(1).skip(1).get!['rows'].map {|row| row['key']}
197
+ #==> ["homepage"]
198
+
199
+ You can also get a count of all clone_ids:
200
+
201
+ HtmlSnippet.count_clone_ids!
202
+ #==> 3
203
+
204
+
205
+ ## Retreiving the clone created farthest in the future for a clone_id group (.map_last_future_by_clone_id)
206
+
207
+ If you'd like to retrieve the latest clone within a clone group, you could of course call `future_clones_by_clone_id` and then call `last` on the resulting array - however, that would be quite silly and idiotically inefficiant. So, instead, call `last_future_clone_by_clone_id`:
208
+
209
+ snippet_1 = HtmlSnippet.create :clone_id => "snippety", :start => Time.now
210
+ snippet_2 = HtmlSnippet.create :clone_id => "snippety", :start => 1000.years.from_now
211
+
212
+ HtmlSnippet.map_last_future_by_clone_id("snippety").get!.first.should == snippet_2
213
+
214
+ After creating these two snippet's, calling `HtmlSnippet.last_future_clone_by_clone_id "snippety"` would return `snippet_2`. However, if we create another "snippety" snippet without a `start` date:
215
+
216
+ snippet_3 = HtmlSnippet.create :clone_id => "snippety"
217
+
218
+ HtmlSnippet.map_last_future_by_clone_id("snippety").get!.first.should == snippet_3
219
+
220
+ Then calling `HtmlSnippet.last_future_clone_by_clone_id "snippety"` would return `snippet3`. Basically, you can imagine clones with a null start date or an empty string start date to have a start scheduled for `infinity + created_at`; in other words, they sort at the end of the map of clones in a clone_id group, and if there are multiple clones without a start date, then they sort by created at (still at the end of the map).
221
+
222
+
223
+ ## CouchPublish Integration
224
+
225
+ The `couch_cloner` gem integrates nicely with the `couch_publish` gem.
226
+
227
+ If you include `CouchCloner` into a gem that already includes `CouchPublish`, then you can use `published` and `unpublished` query modifiers on your query proxies:
228
+
229
+ class HtmlSnippet < CouchRest::Model::Base
230
+ include CouchPublish
231
+ include CouchCloner
232
+
233
+ # etc...
234
+ end
235
+
236
+ HtmlSnippet.map_by_clone_id.key("some-clone-id").published.get!
237
+ HtmlSnippet.count_by_clone_id.key("some-clone-id").unpublished.get!
238
+ HtmlSnippet.map_active_by_clone_id("some-clone-id").published.get!.first
239
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id")
240
+ HtmlSnippet.map_active_and_future_clones_by_clone_id("some-clone-id").unpublished.get!.first
241
+ HtmlSnippet.map_last_future_by_clone_id("some-clone-id").published.get!.first
242
+ HtmlSnippet.clone_ids.unpublished.get!
243
+ HtmlSnippet.count_clone_ids.published.get!
244
+
245
+
246
+ ## CouchVisible Integration
247
+
248
+ The `couch_cloner` gem integrates nicely with the `couch_visible` gem.
249
+
250
+ If you include `CouchCloner` into a gem that already includes `CouchVisible`, then you can pass `:shown => true` and `:hidden => true` options to your `CouchCloner` query methods:
251
+
252
+
253
+ class HtmlSnippet < CouchRest::Model::Base
254
+ include CouchVisible
255
+ include CouchCloner
256
+
257
+ # etc...
258
+ end
259
+
260
+ HtmlSnippet.map_by_clone_id.key("some-clone-id").shown.get!
261
+ HtmlSnippet.count_by_clone_id.key("some-clone-id").hidden.get!
262
+ HtmlSnippet.map_active_by_clone_id("some-clone-id").shown.get!.first
263
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id")
264
+ HtmlSnippet.map_active_and_future_clones_by_clone_id("some-clone-id").hidden.get!.first
265
+ HtmlSnippet.map_last_future_by_clone_id("some-clone-id").shown.get!.first
266
+ HtmlSnippet.clone_ids.hidden.get!
267
+ HtmlSnippet.count_clone_ids.shown.get!
268
+
269
+
270
+ ## CouchPublish and CouchVisible Integration
271
+
272
+ If you include `CouchCloner` into a gem that already includes both `CouchVisible` and `CouchPublish`, then you can, of course, mix and match `unpublished`, `published`, `shown`, and `hidden` query modifiers in your `CouchCloner` query methods:
273
+
274
+ class HtmlSnippet < CouchRest::Model::Base
275
+ include CouchPublish
276
+ include CouchVisible
277
+ include CouchCloner
278
+
279
+ # etc...
280
+ end
281
+
282
+ HtmlSnippet.map_by_clone_id.key("some-clone-id").shown.published.get!
283
+ HtmlSnippet.count_by_clone_id.key("some-clone-id").hidden.get!
284
+ HtmlSnippet.map_active_by_clone_id("some-clone-id").shown.get!.first
285
+ HtmlSnippet.map_by_clone_id_and_start("some_clone_id")
286
+ HtmlSnippet.map_active_and_future_clones_by_clone_id("some-clone-id").hidden.published.get!.first
287
+ HtmlSnippet.map_last_future_by_clone_id("some-clone-id").shown.get!.first
288
+ HtmlSnippet.clone_ids.hidden.get!
289
+ HtmlSnippet.count_clone_ids.shown.unpublished.get!
290
+
291
+ ## PUBLIC DOMAIN
292
+
293
+ This software is public domain. By contributing to it, you agree to let your code contribution enter the public domain.
metadata ADDED
@@ -0,0 +1,222 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: couch_cloner
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matt Parker
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-24 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: cucumber
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: couchrest_model_config
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: couch_publish
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ hash: 25
71
+ segments:
72
+ - 0
73
+ - 0
74
+ - 3
75
+ version: 0.0.3
76
+ type: :development
77
+ version_requirements: *id004
78
+ - !ruby/object:Gem::Dependency
79
+ name: couch_visible
80
+ prerelease: false
81
+ requirement: &id005 !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ type: :development
91
+ version_requirements: *id005
92
+ - !ruby/object:Gem::Dependency
93
+ name: timecop
94
+ prerelease: false
95
+ requirement: &id006 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ type: :development
105
+ version_requirements: *id006
106
+ - !ruby/object:Gem::Dependency
107
+ name: couchrest
108
+ prerelease: false
109
+ requirement: &id007 !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - "="
113
+ - !ruby/object:Gem::Version
114
+ hash: 21
115
+ segments:
116
+ - 1
117
+ - 0
118
+ - 1
119
+ version: 1.0.1
120
+ type: :runtime
121
+ version_requirements: *id007
122
+ - !ruby/object:Gem::Dependency
123
+ name: couchrest_model
124
+ prerelease: false
125
+ requirement: &id008 !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ~>
129
+ - !ruby/object:Gem::Version
130
+ hash: 23
131
+ segments:
132
+ - 1
133
+ - 0
134
+ - 0
135
+ version: 1.0.0
136
+ type: :runtime
137
+ version_requirements: *id008
138
+ - !ruby/object:Gem::Dependency
139
+ name: recloner
140
+ prerelease: false
141
+ requirement: &id009 !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ~>
145
+ - !ruby/object:Gem::Version
146
+ hash: 25
147
+ segments:
148
+ - 0
149
+ - 1
150
+ - 1
151
+ version: 0.1.1
152
+ type: :runtime
153
+ version_requirements: *id009
154
+ - !ruby/object:Gem::Dependency
155
+ name: couch_view
156
+ prerelease: false
157
+ requirement: &id010 !ruby/object:Gem::Requirement
158
+ none: false
159
+ requirements:
160
+ - - ~>
161
+ - !ruby/object:Gem::Version
162
+ hash: 25
163
+ segments:
164
+ - 0
165
+ - 0
166
+ - 3
167
+ version: 0.0.3
168
+ type: :runtime
169
+ version_requirements: *id010
170
+ description: Create clones of CouchDB documents, and schedule them for publication.
171
+ email: moonmaster9000@gmail.com
172
+ executables: []
173
+
174
+ extensions: []
175
+
176
+ extra_rdoc_files: []
177
+
178
+ files:
179
+ - lib/couch_cloner/clone.rb
180
+ - lib/couch_cloner/couch_cloner.rb
181
+ - lib/couch_cloner/maps/by_clone_id_and_start_time.rb
182
+ - lib/couch_cloner/query.rb
183
+ - lib/couch_cloner/scheduling.rb
184
+ - lib/couch_cloner.rb
185
+ - VERSION
186
+ - readme.markdown
187
+ - couch_cloner.gemspec
188
+ homepage: http://github.com/moonmaster9000/couch_cloner
189
+ licenses: []
190
+
191
+ post_install_message:
192
+ rdoc_options: []
193
+
194
+ require_paths:
195
+ - lib
196
+ required_ruby_version: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ hash: 3
202
+ segments:
203
+ - 0
204
+ version: "0"
205
+ required_rubygems_version: !ruby/object:Gem::Requirement
206
+ none: false
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ hash: 3
211
+ segments:
212
+ - 0
213
+ version: "0"
214
+ requirements: []
215
+
216
+ rubyforge_project:
217
+ rubygems_version: 1.8.5
218
+ signing_key:
219
+ specification_version: 3
220
+ summary: Clone and schedule CouchDB documents
221
+ test_files: []
222
+