ibm_sbdtc_rest 0.0.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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 David Ruan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = ibm_sbdtc_rest
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 David Ruan. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,66 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ibm_sbdtc_rest"
8
+ gem.summary = %Q{isbdtc rest api client}
9
+ gem.description = %Q{isbdtc rest api client}
10
+ gem.email = "ruanwz@gmail.com"
11
+ gem.homepage = "http://github.com/ruanwz/ibm_sbdtc_rest"
12
+ gem.authors = ["David Ruan"]
13
+ gem.add_development_dependency "rspec", ">=1.2.9"
14
+ gem.add_development_dependency "cucumber", ">= 0"
15
+ gem.add_development_dependency "jeweler", ">= 1.4.0"
16
+ gem.add_dependency "thor", ">=0.11.8"
17
+ gem.add_dependency "json", ">=1.1.9"
18
+ gem.add_dependency "rest-client", ">=1.0.4"
19
+ gem.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'test/**/*'].to_a
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :spec => :check_dependencies
40
+
41
+ begin
42
+ require 'cucumber/rake/task'
43
+ Cucumber::Rake::Task.new(:features)
44
+
45
+ task :features => :check_dependencies
46
+ rescue LoadError
47
+ task :features do
48
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
49
+ end
50
+ end
51
+
52
+ task :default => :spec
53
+
54
+ require 'rake/rdoctask'
55
+ Rake::RDocTask.new do |rdoc|
56
+ if File.exist?('VERSION')
57
+ version = File.read('VERSION')
58
+ else
59
+ version = ""
60
+ end
61
+
62
+ rdoc.rdoc_dir = 'rdoc'
63
+ rdoc.title = "ibm_sbdtc_rest #{version}"
64
+ rdoc.rdoc_files.include('README*')
65
+ rdoc.rdoc_files.include('lib/**/*.rb')
66
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'thor'
5
+ begin
6
+ if RUBY_VERSION <= '1.8.6'
7
+ require 'ruby-debug'
8
+ end
9
+ rescue LoadError
10
+ end
11
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
12
+ require 'ibm_sbdtc_rest'
13
+ require 'pp'
14
+
15
+ class IbmCloudAdmin < Thor
16
+ map "-h" => :help
17
+ map "-li" => :list_instances
18
+ map "-v" => :version
19
+ map "--list_instances" => :list_instances
20
+
21
+ desc 'list_instances', "list the current instances of IbmCloudAdmin"
22
+ def list_instances
23
+ server = IbmCloudRest.new
24
+ puts JSON.pretty_generate(server.instances)
25
+ end
26
+
27
+ desc 'version', "the current version of IbmCloudAdmin"
28
+ def version
29
+ version_file = File.dirname(__FILE__) + '/../VERSION'
30
+ if File.exists?(version_file) and version = File.read(version_file)
31
+ puts "IbmCloudAdmin version: #{version}"
32
+ end
33
+ end
34
+
35
+ desc 'help', 'help output'
36
+ def help
37
+ puts %{
38
+ Usage: #{$0} /path/to/your/app [options]
39
+
40
+ Options:
41
+ -g, --generate Run the generate command to build a project
42
+ -li, --list_instances List all instances
43
+ -j, --just_recipe Run the just_recipe command to only run specified recipes, templates, etc.
44
+ -d, --display Instead of running, show the template or recipe body
45
+ -r, --recipes Recipes to use
46
+ -s, --save Save current set of options
47
+ -u, --use Use a saved set of options
48
+ --gems Gems to include
49
+
50
+ IbmCloudAdmin Info:
51
+ -v, --version Show the IbmCloudAdmin version number and quit.
52
+ -l, --list Show the various recipes known to IbmCloudAdmin and quit.
53
+ -h, --help Show this help message and quit.
54
+
55
+ General Options:
56
+
57
+ Description:
58
+ IbmCloudAdmin is used for Ibm Cloud Service.
59
+
60
+ Example:
61
+ }
62
+ end
63
+ end
64
+ def method_missing(*args)
65
+ unless @activesupport_required
66
+ require 'activesupport'
67
+ @activesupport_required = true
68
+ m = args.shift
69
+ send(m, *args)
70
+ else
71
+ super
72
+ end
73
+ end
74
+
75
+ IbmCloudAdmin.start
76
+
@@ -0,0 +1,45 @@
1
+ module RestClientAdapter
2
+
3
+ module API
4
+ def read_config
5
+ if File.exists? File.expand_path('~/.isbdtcrc')
6
+ return YAML.load(File.new(File.expand_path('~/.isbdtcrc')))
7
+ else
8
+ raise "Error while read config file"
9
+ end
10
+ end
11
+ def proxy=(url)
12
+ RestClient.proxy = url
13
+ end
14
+
15
+ def proxy
16
+ RestClient.proxy
17
+ end
18
+
19
+ def get(uri, headers={:accept => 'application/json'})
20
+ #RestClient.get(uri, headers)
21
+ config = read_config
22
+ rest_get=RestClient::Request.new(:method => :get, :url => uri, :headers => headers,:user => config['user'], :password => config['password'])
23
+ rest_get.execute
24
+ end
25
+
26
+ def post(uri, payload, headers={:accept => 'application/json'})
27
+ RestClient.post(uri, payload, headers)
28
+ end
29
+
30
+ def put(uri, payload, headers={:accept => 'application/json'})
31
+ RestClient.put(uri, payload, headers)
32
+ end
33
+
34
+ def delete(uri, headers={:accept => 'application/json'})
35
+ RestClient.delete(uri, headers)
36
+ end
37
+
38
+ def copy(uri, headers)
39
+ RestClient::Request.execute( :method => :copy,
40
+ :url => uri,
41
+ :headers => headers)
42
+ end
43
+ end
44
+
45
+ end
File without changes
@@ -0,0 +1,328 @@
1
+ require 'cgi'
2
+ require "base64"
3
+
4
+ module IbmCloudRest
5
+ class Database
6
+ attr_reader :server, :host, :name, :root, :uri
7
+ attr_accessor :bulk_save_cache_limit
8
+
9
+ # Create a IbmCloudRest::Database adapter for the supplied IbmCloudRest::Server
10
+ # and database name.
11
+ #
12
+ # ==== Parameters
13
+ # server<IbmCloudRest::Server>:: database host
14
+ # name<String>:: database name
15
+ #
16
+ def initialize(server, name)
17
+ @name = name
18
+ @server = server
19
+ @host = server.uri
20
+ @uri = "/#{name.gsub('/','%2F')}"
21
+ @root = host + uri
22
+ @streamer = Streamer.new(self)
23
+ @bulk_save_cache = []
24
+ @bulk_save_cache_limit = 500 # must be smaller than the uuid count
25
+ end
26
+
27
+ # returns the database's uri
28
+ def to_s
29
+ @root
30
+ end
31
+
32
+ # GET the database info from CouchDB
33
+ def info
34
+ IbmCloudRest.get @root
35
+ end
36
+
37
+ # Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
38
+ def documents(params = {})
39
+ keys = params.delete(:keys)
40
+ url = IbmCloudRest.paramify_url "#{@root}/_all_docs", params
41
+ if keys
42
+ IbmCloudRest.post(url, {:keys => keys})
43
+ else
44
+ IbmCloudRest.get url
45
+ end
46
+ end
47
+
48
+ # load a set of documents by passing an array of ids
49
+ def get_bulk(ids)
50
+ documents(:keys => ids, :include_docs => true)
51
+ end
52
+ alias :bulk_load :get_bulk
53
+
54
+ # POST a temporary view function to CouchDB for querying. This is not
55
+ # recommended, as you don't get any performance benefit from CouchDB's
56
+ # materialized views. Can be quite slow on large databases.
57
+ def slow_view(funcs, params = {})
58
+ keys = params.delete(:keys)
59
+ funcs = funcs.merge({:keys => keys}) if keys
60
+ url = IbmCloudRest.paramify_url "#{@root}/_temp_view", params
61
+ JSON.parse(HttpAbstraction.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
62
+ end
63
+
64
+ # backwards compatibility is a plus
65
+ alias :temp_view :slow_view
66
+
67
+ # Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
68
+ # paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
69
+ def view(name, params = {}, &block)
70
+ keys = params.delete(:keys)
71
+ name = name.split('/') # I think this will always be length == 2, but maybe not...
72
+ dname = name.shift
73
+ vname = name.join('/')
74
+ url = IbmCloudRest.paramify_url "#{@root}/_design/#{dname}/_view/#{vname}", params
75
+ if keys
76
+ IbmCloudRest.post(url, {:keys => keys})
77
+ else
78
+ if block_given?
79
+ @streamer.view("_design/#{dname}/_view/#{vname}", params, &block)
80
+ else
81
+ IbmCloudRest.get url
82
+ end
83
+ end
84
+ end
85
+
86
+ # GET a document from CouchDB, by id. Returns a Ruby Hash.
87
+ def get(id, params = {})
88
+ slug = escape_docid(id)
89
+ url = IbmCloudRest.paramify_url("#{@root}/#{slug}", params)
90
+ result = IbmCloudRest.get(url)
91
+ return result unless result.is_a?(Hash)
92
+ doc = if /^_design/ =~ result["_id"]
93
+ Design.new(result)
94
+ else
95
+ Document.new(result)
96
+ end
97
+ doc.database = self
98
+ doc
99
+ end
100
+
101
+ # GET an attachment directly from CouchDB
102
+ def fetch_attachment(doc, name)
103
+ uri = url_for_attachment(doc, name)
104
+ HttpAbstraction.get uri
105
+ end
106
+
107
+ # PUT an attachment directly to CouchDB
108
+ def put_attachment(doc, name, file, options = {})
109
+ docid = escape_docid(doc['_id'])
110
+ name = CGI.escape(name)
111
+ uri = url_for_attachment(doc, name)
112
+ JSON.parse(HttpAbstraction.put(uri, file, options))
113
+ end
114
+
115
+ # DELETE an attachment directly from CouchDB
116
+ def delete_attachment(doc, name, force=false)
117
+ uri = url_for_attachment(doc, name)
118
+ # this needs a rev
119
+ begin
120
+ JSON.parse(HttpAbstraction.delete(uri))
121
+ rescue Exception => error
122
+ if force
123
+ # get over a 409
124
+ doc = get(doc['_id'])
125
+ uri = url_for_attachment(doc, name)
126
+ JSON.parse(HttpAbstraction.delete(uri))
127
+ else
128
+ error
129
+ end
130
+ end
131
+ end
132
+
133
+ # Save a document to CouchDB. This will use the <tt>_id</tt> field from
134
+ # the document as the id for PUT, or request a new UUID from CouchDB, if
135
+ # no <tt>_id</tt> is present on the document. IDs are attached to
136
+ # documents on the client side because POST has the curious property of
137
+ # being automatically retried by proxies in the event of network
138
+ # segmentation and lost responses.
139
+ #
140
+ # If <tt>bulk</tt> is true (false by default) the document is cached for bulk-saving later.
141
+ # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
142
+ def save_doc(doc, bulk = false)
143
+ if doc['_attachments']
144
+ doc['_attachments'] = encode_attachments(doc['_attachments'])
145
+ end
146
+ if bulk
147
+ @bulk_save_cache << doc
148
+ return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
149
+ return {"ok" => true} # Compatibility with Document#save
150
+ elsif !bulk && @bulk_save_cache.length > 0
151
+ bulk_save
152
+ end
153
+ result = if doc['_id']
154
+ slug = escape_docid(doc['_id'])
155
+ begin
156
+ IbmCloudRest.put "#{@root}/#{slug}", doc
157
+ rescue HttpAbstraction::ResourceNotFound
158
+ p "resource not found when saving even tho an id was passed"
159
+ slug = doc['_id'] = @server.next_uuid
160
+ IbmCloudRest.put "#{@root}/#{slug}", doc
161
+ end
162
+ else
163
+ begin
164
+ slug = doc['_id'] = @server.next_uuid
165
+ IbmCloudRest.put "#{@root}/#{slug}", doc
166
+ rescue #old version of couchdb
167
+ IbmCloudRest.post @root, doc
168
+ end
169
+ end
170
+ if result['ok']
171
+ doc['_id'] = result['id']
172
+ doc['_rev'] = result['rev']
173
+ doc.database = self if doc.respond_to?(:database=)
174
+ end
175
+ result
176
+ end
177
+
178
+ ### DEPRECATION NOTICE
179
+ def save(doc, bulk=false)
180
+ puts "IbmCloudRest::Database's save method is being deprecated, please use save_doc instead"
181
+ save_doc(doc, bulk)
182
+ end
183
+
184
+
185
+ # POST an array of documents to CouchDB. If any of the documents are
186
+ # missing ids, supply one from the uuid cache.
187
+ #
188
+ # If called with no arguments, bulk saves the cache of documents to be bulk saved.
189
+ def bulk_save(docs = nil, use_uuids = true)
190
+ if docs.nil?
191
+ docs = @bulk_save_cache
192
+ @bulk_save_cache = []
193
+ end
194
+ if (use_uuids)
195
+ ids, noids = docs.partition{|d|d['_id']}
196
+ uuid_count = [noids.length, @server.uuid_batch_count].max
197
+ noids.each do |doc|
198
+ nextid = @server.next_uuid(uuid_count) rescue nil
199
+ doc['_id'] = nextid if nextid
200
+ end
201
+ end
202
+ IbmCloudRest.post "#{@root}/_bulk_docs", {:docs => docs}
203
+ end
204
+ alias :bulk_delete :bulk_save
205
+
206
+ # DELETE the document from CouchDB that has the given <tt>_id</tt> and
207
+ # <tt>_rev</tt>.
208
+ #
209
+ # If <tt>bulk</tt> is true (false by default) the deletion is recorded for bulk-saving (bulk-deletion :) later.
210
+ # Bulk saving happens automatically when #bulk_save_cache limit is exceded, or on the next non bulk save.
211
+ def delete_doc(doc, bulk = false)
212
+ raise ArgumentError, "_id and _rev required for deleting" unless doc['_id'] && doc['_rev']
213
+ if bulk
214
+ @bulk_save_cache << { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true }
215
+ return bulk_save if @bulk_save_cache.length >= @bulk_save_cache_limit
216
+ return { "ok" => true } # Mimic the non-deferred version
217
+ end
218
+ slug = escape_docid(doc['_id'])
219
+ IbmCloudRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
220
+ end
221
+
222
+ ### DEPRECATION NOTICE
223
+ def delete(doc, bulk=false)
224
+ puts "IbmCloudRest::Database's delete method is being deprecated, please use delete_doc instead"
225
+ delete_doc(doc, bulk)
226
+ end
227
+
228
+ # COPY an existing document to a new id. If the destination id currently exists, a rev must be provided.
229
+ # <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
230
+ # hash with a '_rev' key
231
+ def copy_doc(doc, dest)
232
+ raise ArgumentError, "_id is required for copying" unless doc['_id']
233
+ slug = escape_docid(doc['_id'])
234
+ destination = if dest.respond_to?(:has_key?) && dest['_id'] && dest['_rev']
235
+ "#{dest['_id']}?rev=#{dest['_rev']}"
236
+ else
237
+ dest
238
+ end
239
+ IbmCloudRest.copy "#{@root}/#{slug}", destination
240
+ end
241
+
242
+ ### DEPRECATION NOTICE
243
+ def copy(doc, dest)
244
+ puts "IbmCloudRest::Database's copy method is being deprecated, please use copy_doc instead"
245
+ copy_doc(doc, dest)
246
+ end
247
+
248
+ # Compact the database, removing old document revisions and optimizing space use.
249
+ def compact!
250
+ IbmCloudRest.post "#{@root}/_compact"
251
+ end
252
+
253
+ # Create the database
254
+ def create!
255
+ bool = server.create_db(@name) rescue false
256
+ bool && true
257
+ end
258
+
259
+ # Delete and re create the database
260
+ def recreate!
261
+ delete!
262
+ create!
263
+ rescue HttpAbstraction::ResourceNotFound
264
+ ensure
265
+ create!
266
+ end
267
+
268
+ # Replicates via "pulling" from another database to this database. Makes no attempt to deal with conflicts.
269
+ def replicate_from other_db
270
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(IbmCloudRest::Database)
271
+ IbmCloudRest.post "#{@host}/_replicate", :source => other_db.root, :target => name
272
+ end
273
+
274
+ # Replicates via "pushing" to another database. Makes no attempt to deal with conflicts.
275
+ def replicate_to other_db
276
+ raise ArgumentError, "must provide a CouchReset::Database" unless other_db.kind_of?(IbmCloudRest::Database)
277
+ IbmCloudRest.post "#{@host}/_replicate", :target => other_db.root, :source => name
278
+ end
279
+
280
+ # DELETE the database itself. This is not undoable and could be rather
281
+ # catastrophic. Use with care!
282
+ def delete!
283
+ clear_extended_doc_fresh_cache
284
+ IbmCloudRest.delete @root
285
+ end
286
+
287
+ private
288
+
289
+ def clear_extended_doc_fresh_cache
290
+ ::IbmCloudRest::ExtendedDocument.subclasses.each{|klass| klass.design_doc_fresh = false if klass.respond_to?(:design_doc_fresh=) }
291
+ end
292
+
293
+ def uri_for_attachment(doc, name)
294
+ if doc.is_a?(String)
295
+ puts "IbmCloudRest::Database#fetch_attachment will eventually require a doc as the first argument, not a doc.id"
296
+ docid = doc
297
+ rev = nil
298
+ else
299
+ docid = doc['_id']
300
+ rev = doc['_rev']
301
+ end
302
+ docid = escape_docid(docid)
303
+ name = CGI.escape(name)
304
+ rev = "?rev=#{doc['_rev']}" if rev
305
+ "/#{docid}/#{name}#{rev}"
306
+ end
307
+
308
+ def url_for_attachment(doc, name)
309
+ @root + uri_for_attachment(doc, name)
310
+ end
311
+
312
+ def escape_docid id
313
+ /^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
314
+ end
315
+
316
+ def encode_attachments(attachments)
317
+ attachments.each do |k,v|
318
+ next if v['stub']
319
+ v['data'] = base64(v['data'])
320
+ end
321
+ attachments
322
+ end
323
+
324
+ def base64(data)
325
+ Base64.encode64(data).gsub(/\s/,'')
326
+ end
327
+ end
328
+ end