cloudant 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/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cloudant.gemspec +28 -0
- data/lib/cloudant.rb +9 -0
- data/lib/cloudant/client.rb +225 -0
- data/lib/cloudant/connection.rb +62 -0
- data/lib/cloudant/query_builder.rb +30 -0
- data/lib/cloudant/version.rb +3 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9a87df45b55aa852bb16fbfaf68bb0846a3171f7
|
4
|
+
data.tar.gz: ea5973b00b7892677d321898c28cf5b8bde56ad8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e31e0d97dec43099293c76f4e24f83026be30aed88c12250ccbae1e41c2a09f9ce6b40d5a72f48e384210bb821cf778eee32c0cb2e070816c4509decc2783bde
|
7
|
+
data.tar.gz: 3f5c0abb9ccd099505256e0a03c20fd9fc6beff6902f7fe2ba5162f3ee5b60c7b65d94129aa40646fa634e02114dedadf26b9a56ee96d4e259247516d1b79b8a
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Alex Yanai
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# Cloudant
|
2
|
+
|
3
|
+
[](https://travis-ci.org/AlexYanai/cloudant)
|
4
|
+
[](https://coveralls.io/github/AlexYanai/cloudant?branch=master)
|
5
|
+
[](https://github.com/AlexYanai/cloudant/blob/master/LICENSE.txt)
|
6
|
+
|
7
|
+
Ruby Cloudant is a simple Ruby interface for [IBM Cloudant's](https://cloudant.com/) API. Cloudant is a NoSQL database built on [CouchDB](http://couchdb.apache.org/).
|
8
|
+
|
9
|
+
This gem is still in [development](##Contributing) and is a work in progress.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'cloudant'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install cloudant
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
Create a Cloudant account and find your credentials.
|
29
|
+
|
30
|
+
**Creating a new client**
|
31
|
+
|
32
|
+
If valid credentials are provided a `POST` request will be made to `/_session` and the user will be logged in via [cookie authentication](https://docs.cloudant.com/authentication.html#cookie-authentication)
|
33
|
+
```ruby
|
34
|
+
credentials = {
|
35
|
+
:username => ENV['username'],
|
36
|
+
:password => ENV['password'],
|
37
|
+
:database => ENV['database'] # Can leave blank.
|
38
|
+
}
|
39
|
+
|
40
|
+
client = Cloudant::Client.new(credentials)
|
41
|
+
```
|
42
|
+
|
43
|
+
Ending a session
|
44
|
+
```ruby
|
45
|
+
client.close
|
46
|
+
```
|
47
|
+
|
48
|
+
**Setting and changing databases**
|
49
|
+
|
50
|
+
If this is your first time accessing a new Cloudant instance, or don't have a database existing, create one:
|
51
|
+
```ruby
|
52
|
+
client.all_dbs # See existing databases
|
53
|
+
client.create_db(database_name)
|
54
|
+
```
|
55
|
+
|
56
|
+
Or delete an existing database:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
client.delete_db(database_name)
|
60
|
+
```
|
61
|
+
|
62
|
+
You can then set (or change, if one is already set) a new database:
|
63
|
+
```ruby
|
64
|
+
client.database = database_name
|
65
|
+
client.db_info # See database information, including name and size
|
66
|
+
```
|
67
|
+
|
68
|
+
**Basic Usage**
|
69
|
+
```ruby
|
70
|
+
# Create a single document
|
71
|
+
doc = {'_id'=>'1', 'field_one'=>'your content'}
|
72
|
+
response = client.create(doc)
|
73
|
+
# => {'ok'=>true, 'id'=>'1', 'rev'=>'1-be74a68c2105f3d9d0245eb8736ca9f1'}
|
74
|
+
|
75
|
+
# Find a single document by id
|
76
|
+
doc_id = response['id']
|
77
|
+
client.doc(doc_id)
|
78
|
+
|
79
|
+
# Update a single document (requires an existing doc's id and current rev)
|
80
|
+
new_doc = {'id'=>'1', 'rev'=>'1-bf74a68c2105f3d9d0245fc836ca9f3', 'field_two'=>'more content'}
|
81
|
+
client.update(new_doc)
|
82
|
+
|
83
|
+
# Delete a single doc
|
84
|
+
client.delete_doc(doc_id)
|
85
|
+
```
|
86
|
+
|
87
|
+
**Multiple Documents**
|
88
|
+
```ruby
|
89
|
+
# Creating multiple documents
|
90
|
+
docs = [
|
91
|
+
{'_id'=>'1', 'field_one' => 'your content'},
|
92
|
+
{'_id'=>'2', 'field_two' => 'more content'},
|
93
|
+
{'_id'=>'3', 'field_three' => 'new content'}
|
94
|
+
]
|
95
|
+
|
96
|
+
client.create_docs(docs)
|
97
|
+
|
98
|
+
# Updating multiple docs
|
99
|
+
# Note: updating and creating multiple documents are equivalent
|
100
|
+
client.update_docs(docs)
|
101
|
+
|
102
|
+
# Deleting multiple docs
|
103
|
+
client.delete_docs(docs)
|
104
|
+
```
|
105
|
+
`:delete_docs` is a convenience method; deleting multiple documents at once is best performed by adding `['_deleted']` to each document and performing an update. [Bulk operations](https://docs.cloudant.com/document.html#bulk-operations)
|
106
|
+
|
107
|
+
**Indices, Design Docs**
|
108
|
+
```ruby
|
109
|
+
# Create a new index
|
110
|
+
client.create_index({index: {}, type: 'text', name: 'test_index'})
|
111
|
+
|
112
|
+
# See all indices
|
113
|
+
client.get_indices # Or client.get_indexes
|
114
|
+
|
115
|
+
# Create a new design doc named 'test'
|
116
|
+
ddoc = {'language' => 'javascript', 'views' => {} }
|
117
|
+
client.create_design_doc('test',ddoc)
|
118
|
+
```
|
119
|
+
|
120
|
+
**[Querying](https://docs.cloudant.com/cloudant_query.html#finding-documents-using-an-index)**
|
121
|
+
```ruby
|
122
|
+
# Perform a single query
|
123
|
+
q = {'selector': {'test_field': {'$exists': true}},'fields': ['_id', '_rev'],'limit': 1,'skip': 0}
|
124
|
+
client.query(q)
|
125
|
+
|
126
|
+
# Performing a paginated query using a bookmark
|
127
|
+
q = {"selector": {"test_field": {"$exists": true}},"fields": ["_id", "_rev"],"limit": 1,"skip": 0}
|
128
|
+
client.bookmark_query(q) do |docs|
|
129
|
+
# Do something with the returned documents
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
**[Views](https://docs.cloudant.com/creating_views.html#using-views)**
|
134
|
+
```ruby
|
135
|
+
# Creating a view
|
136
|
+
client.create_view('test',{"current"=>{"reduce"=>"_count","map"=>"function (doc) {\n if (doc.status === \"name\") {\n emit(doc._id,1);\n }\n}"}})
|
137
|
+
|
138
|
+
# Querying an existing view
|
139
|
+
database = 'test'
|
140
|
+
view_to_query = 'current'
|
141
|
+
|
142
|
+
client.view(database,view_to_query) # If a reduce function is given will return a 'rows' array with a single record, the value of the reduce
|
143
|
+
# => {"rows"=>[{"key"=>nil, "value"=>2}]}
|
144
|
+
|
145
|
+
client.view(database,view_to_query, :reduce => false, :include_docs => true)
|
146
|
+
# => {"total_rows"=>2, "offset"=>0, "rows"=>[{"id"=>"5d8e6c99198dfdde8accd8e019ba052", "key"=>"5d8e6c99198dfdde8accd8e019ba052", "value"=>1, "doc"=>{"_id"=>"5d8e6c99198dfdde8accd8e019ba052", "_rev"=>"1-7ebdb5b82e1cc4eaf2e27a711e9857c6", "a"=>10, "b"=>92, "c"=>31}}, {"id"=>"5d8e6c99898dcdd08accd8e019badab", "key"=>"5d8e6c99898dcdd0daccd8e019badab", "value"=>1, "doc"=>{"_id"=>"5d8e6c99898dcdd8daccd8e019badab", "_rev"=>"1-d36298f4391da575df61e170af2efa34", "b"=>12, "c"=>33}}]}
|
147
|
+
```
|
148
|
+
|
149
|
+
## To Do
|
150
|
+
- Add database replication functionality - `/_replicator`
|
151
|
+
- Allow new user creation - `/_security`
|
152
|
+
- Add more robust options handling for various queries (expanding the `QueryBuilder` module, as used in view querying)
|
153
|
+
- Currently, options have to be added to a query string by the user.
|
154
|
+
- Add support for `attachments`
|
155
|
+
|
156
|
+
## Contributing
|
157
|
+
|
158
|
+
1. Fork it
|
159
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
160
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
161
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
162
|
+
5. Create new Pull Request
|
163
|
+
|
164
|
+
## License
|
165
|
+
|
166
|
+
Copyright (c) 2016 Alex Yanai. Released under the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "cloudant"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/cloudant.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cloudant/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cloudant"
|
8
|
+
spec.version = Cloudant::VERSION
|
9
|
+
spec.authors = ["Alex Yanai"]
|
10
|
+
spec.email = ["yanai.alex@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Ruby interface to Cloudant's API.}
|
13
|
+
spec.description = %q{A ruby interface to Cloudant's API.}
|
14
|
+
spec.homepage = "https://github.com/AlexYanai/cloudant"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
spec.add_development_dependency "dotenv", "~> 2.1"
|
26
|
+
spec.add_development_dependency "webmock", "~> 2.1"
|
27
|
+
spec.add_development_dependency "rest-client", "~> 2.0"
|
28
|
+
end
|
data/lib/cloudant.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
module Cloudant
|
2
|
+
class Client
|
3
|
+
attr_reader :username, :password
|
4
|
+
attr_accessor :database, :base_uri
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@username = args[:username]
|
8
|
+
@password = args[:password]
|
9
|
+
@database = args[:database]
|
10
|
+
@base_uri = "https://#{username}.cloudant.com/"
|
11
|
+
@conn = start_connection(username,password,base_uri)
|
12
|
+
|
13
|
+
@conn.cookie_auth
|
14
|
+
end
|
15
|
+
|
16
|
+
# Retrieve all docs from the database
|
17
|
+
# Returns an array of hashes including ids, keys, and revs, unless :include_docs is passed
|
18
|
+
def all_docs(*include_docs)
|
19
|
+
q = "#{database}/_all_docs"
|
20
|
+
q << "?include_docs=true" if include_docs && include_docs.any? && include_docs.first == :include_docs
|
21
|
+
|
22
|
+
@conn.query({url_path: q, method: :get})
|
23
|
+
end
|
24
|
+
|
25
|
+
# Accepts a single document id and returns it if found
|
26
|
+
def get_doc(id)
|
27
|
+
@conn.query({url_path: "#{database}/#{id}", method: :get})
|
28
|
+
end
|
29
|
+
alias_method :get, :get_doc
|
30
|
+
alias_method :doc, :get_doc
|
31
|
+
|
32
|
+
# A valid doc must be provided. The doc must be a hash that can.
|
33
|
+
# Use create_docs to create multiple documents at once.
|
34
|
+
def create_doc(doc)
|
35
|
+
@conn.query({url_path: "#{database}", opts: doc, method: :post})
|
36
|
+
end
|
37
|
+
alias_method :create, :create_doc
|
38
|
+
alias_method :post, :create_doc
|
39
|
+
|
40
|
+
# Returns an error hash if a valid id isn't given
|
41
|
+
def update_doc(doc)
|
42
|
+
id = doc["_id"] if doc["_id"]
|
43
|
+
|
44
|
+
@conn.query({url_path: "#{database}/#{id}", opts: doc, method: :put})
|
45
|
+
end
|
46
|
+
alias_method :put, :update_doc
|
47
|
+
|
48
|
+
# Intended behavior for this method to accept only an id to delete a doc.
|
49
|
+
# TODO: Add an optional param for rev.
|
50
|
+
def delete_doc(id)
|
51
|
+
doc = get_doc(id)
|
52
|
+
rev = doc["_rev"] if doc["_rev"]
|
53
|
+
|
54
|
+
@conn.query({url_path: "#{database}/#{id}?rev=#{rev}", method: :delete})
|
55
|
+
end
|
56
|
+
alias_method :delete, :delete_doc
|
57
|
+
|
58
|
+
# Convenience method: this is functionally equivalent to get_doc if
|
59
|
+
# "/_design" is prepended to the id.
|
60
|
+
# ie: get_doc("/_design/:id") == get_design_doc(":id")
|
61
|
+
def get_design_doc(id)
|
62
|
+
@conn.query({url_path: "#{database}/_design/#{id}", method: :get})
|
63
|
+
end
|
64
|
+
alias_method :ddoc, :get_design_doc
|
65
|
+
|
66
|
+
# Need to provide valid design doc or returns an error hash.
|
67
|
+
def create_design_doc(id,doc)
|
68
|
+
@conn.query({url_path: "#{database}/_design/#{id}", opts: doc, method: :put})
|
69
|
+
end
|
70
|
+
alias_method :update_design_doc, :create_design_doc
|
71
|
+
alias_method :create_ddoc, :create_design_doc
|
72
|
+
|
73
|
+
# Intended behavior for this method to accept only an id to delete a doc.
|
74
|
+
# TODO: Add an optional param for rev.
|
75
|
+
def delete_design_doc(id)
|
76
|
+
doc = get_design_doc(id)
|
77
|
+
rev = doc["_rev"] if doc && doc["_rev"]
|
78
|
+
|
79
|
+
@conn.query({url_path: "#{database}/_design/#{id}?rev=#{rev}", opts: doc, method: :delete})
|
80
|
+
end
|
81
|
+
|
82
|
+
# Id of the design doc in which the view (doc) will be held.
|
83
|
+
# Views must be held in design docs; if no design doc matches the id
|
84
|
+
# provided, one will be created with said id.
|
85
|
+
def create_view(id,doc)
|
86
|
+
resp = get_design_doc(id)
|
87
|
+
ddoc = set_views(resp,doc)
|
88
|
+
|
89
|
+
create_design_doc(id,ddoc)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get a hash {"results" => []}, containing a hash of seq, id, changes
|
93
|
+
def changes
|
94
|
+
@conn.query({url_path: "#{database}/_changes", method: :get})
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns all database for the current instance of Cloudant
|
98
|
+
def all_dbs
|
99
|
+
@conn.query({url_path: "_all_dbs", method: :get})
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns info about the database, including update_seq, db_name etc.
|
103
|
+
def db_info
|
104
|
+
@conn.query({url_path: "#{database}", method: :get})
|
105
|
+
end
|
106
|
+
alias_method :info, :db_info
|
107
|
+
|
108
|
+
# Create a new database for the current Cloudant instance
|
109
|
+
def create_db(database)
|
110
|
+
@conn.query({url_path: database, method: :put})
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete_db(database)
|
114
|
+
@conn.query({url_path: database, method: :delete})
|
115
|
+
end
|
116
|
+
|
117
|
+
# Create a new index. A valid index must be given.
|
118
|
+
# Note: An index will be created if only a name is provided (see below)
|
119
|
+
def create_index(args)
|
120
|
+
if args[:name]
|
121
|
+
new_index = create_new_index(args)
|
122
|
+
|
123
|
+
@conn.query({url_path: "#{database}/_index", opts: new_index, method: :post})
|
124
|
+
else
|
125
|
+
raise ArgumentError.new('name is required')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# If only a name is provided the default index doc is {"type": "text","index": {}}
|
130
|
+
# The default index, {}, will index all fields in all docs. This may take a long
|
131
|
+
# time with large databases.
|
132
|
+
def create_new_index(args)
|
133
|
+
new_index = {}
|
134
|
+
|
135
|
+
args[:index] ? new_index["index"] = args[:index] : new_index["index"] = {}
|
136
|
+
|
137
|
+
new_index["name"] = args[:name] if args[:name]
|
138
|
+
new_index["ddoc"] = args[:ddoc] if args[:ddoc]
|
139
|
+
|
140
|
+
args[:type] ? new_index["type"] = args[:type] : new_index["type"] = "text"
|
141
|
+
|
142
|
+
new_index
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns all in order of creation
|
146
|
+
def get_indices
|
147
|
+
@conn.query({url_path: "#{database}/_index", method: :get})
|
148
|
+
end
|
149
|
+
alias_method :get_indexes, :get_indices
|
150
|
+
|
151
|
+
# Delete an index
|
152
|
+
def delete_index(args)
|
153
|
+
@conn.query({url_path: "#{database}/_index/#{args[:ddoc]}/#{args[:type]}/#{args[:name]}", method: :delete})
|
154
|
+
end
|
155
|
+
|
156
|
+
# Use an existing view. Accepts an options hash containing valid args for a query string.
|
157
|
+
# If no options are given the returned value will be the value of the view's reduce function,
|
158
|
+
# if it has one, or rows containing keys, ids, and values if not.
|
159
|
+
def view(ddoc,view,*opts)
|
160
|
+
q = "#{database}/_design/#{ddoc}/_view/#{view}"
|
161
|
+
q << QueryBuilder.build(opts.first,"view") if opts && opts.any? && opts.first.is_a?(Hash)
|
162
|
+
|
163
|
+
@conn.query({url_path: q, method: :get})
|
164
|
+
end
|
165
|
+
|
166
|
+
# Accepts an array of docs. Ids and revs are optional for creation but required for update.
|
167
|
+
def create_docs(docs_array)
|
168
|
+
bulk_docs(docs_array)
|
169
|
+
end
|
170
|
+
alias_method :update_docs, :create_docs
|
171
|
+
|
172
|
+
# Requires the original doc including id and rev fields.
|
173
|
+
# Accepts and array of docs. Unlike :delete_doc, this doesn't make
|
174
|
+
# a request to get the docs beforehand and won't accept just ids.
|
175
|
+
def delete_docs(docs_array)
|
176
|
+
docs_array.each { |doc| doc["_deleted"] = true }
|
177
|
+
bulk_docs(docs_array)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Query the database. Returns all found results at once.
|
181
|
+
# TODO: Expand query functionality.
|
182
|
+
def query(q)
|
183
|
+
@conn.query({url_path: "#{database}/_find", opts: q, method: :post})
|
184
|
+
end
|
185
|
+
|
186
|
+
# Paginate query results - best for large volume.
|
187
|
+
# TODO: add feature that allows users to view previous pages and generall move into own class.
|
188
|
+
def bookmark_query(q,&blk)
|
189
|
+
response = query(q)
|
190
|
+
bookmark = response["bookmark"]
|
191
|
+
docs = response["docs"]
|
192
|
+
|
193
|
+
until !docs || docs.empty?
|
194
|
+
yield docs
|
195
|
+
q["bookmark"] = bookmark
|
196
|
+
response = query(q)
|
197
|
+
bookmark = response["bookmark"]
|
198
|
+
docs = response["docs"]
|
199
|
+
end
|
200
|
+
|
201
|
+
docs
|
202
|
+
end
|
203
|
+
|
204
|
+
# Delete the current cookie.
|
205
|
+
def close
|
206
|
+
@conn.close
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
def start_connection(username,password,base_uri)
|
211
|
+
Connection.new({username: username, password: password, base_uri: base_uri})
|
212
|
+
end
|
213
|
+
|
214
|
+
def bulk_docs(docs)
|
215
|
+
opts = { "docs" => docs }
|
216
|
+
@conn.query({url_path: "#{database}/_bulk_docs", opts: opts, method: :post})
|
217
|
+
end
|
218
|
+
|
219
|
+
def set_views(response,doc)
|
220
|
+
response["views"] = {} unless response["views"]
|
221
|
+
response["views"] = response["views"].merge(doc)
|
222
|
+
response
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Cloudant
|
2
|
+
class Connection
|
3
|
+
attr_accessor :cookies
|
4
|
+
attr_reader :username, :password, :base_uri
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@username = args[:username]
|
8
|
+
@password = args[:password]
|
9
|
+
@base_uri = args[:base_uri]
|
10
|
+
end
|
11
|
+
|
12
|
+
def query(args)
|
13
|
+
base = URI.parse("#{base_uri}#{args[:url_path]}")
|
14
|
+
http = Net::HTTP.new(base.host, 80)
|
15
|
+
request = new_net_http_crud(args[:method],base)
|
16
|
+
|
17
|
+
args[:opts] ? opts = args[:opts] : opts = { "Cookie" => cookies }
|
18
|
+
|
19
|
+
request.body = JSON.generate(opts)
|
20
|
+
request['Cookie'] = cookies
|
21
|
+
request["Content-Type"] = "application/json"
|
22
|
+
response = http.request(request)
|
23
|
+
|
24
|
+
JSON.parse(response.body)
|
25
|
+
end
|
26
|
+
|
27
|
+
def cookie_auth
|
28
|
+
response = start_session
|
29
|
+
@cookies = get_cookies(response)
|
30
|
+
end
|
31
|
+
|
32
|
+
def close
|
33
|
+
query({url_path: "_session", method: :delete})
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def new_net_http_crud(method,base)
|
38
|
+
method = method.to_sym.capitalize
|
39
|
+
Net::HTTP.const_get(method).new(base.request_uri)
|
40
|
+
end
|
41
|
+
|
42
|
+
def start_session
|
43
|
+
RestClient.post("#{base_uri}_session", session_params)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_cookies(response)
|
47
|
+
headers = response.headers
|
48
|
+
cookies = headers[:set_cookie] if headers && headers.is_a?(Hash)
|
49
|
+
cookies.is_a?(Array) ? cookie = cookies.first : cookie = cookies
|
50
|
+
cookie
|
51
|
+
end
|
52
|
+
|
53
|
+
def session_params
|
54
|
+
{
|
55
|
+
username: username,
|
56
|
+
password: password,
|
57
|
+
"Content-Type" => "application/x-www-form-urlencoded",
|
58
|
+
"Accept" => "*/*"
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cloudant
|
2
|
+
module QueryBuilder
|
3
|
+
class << self
|
4
|
+
# TODO: Add a check to determine if options value is valid for key.
|
5
|
+
# ex: {include_docs: true} is valid, {include_docs: 6} is not.
|
6
|
+
def build(opts,type)
|
7
|
+
query_str = ""
|
8
|
+
fields = get_fields(type)
|
9
|
+
|
10
|
+
fields.each do |field|
|
11
|
+
key = field
|
12
|
+
val = opts[field].to_s
|
13
|
+
|
14
|
+
current = "#{key}=#{val}" if val != ""
|
15
|
+
query_str << "&" << current if (query_str != "" && current)
|
16
|
+
query_str << "?" << current if (query_str == "" && current)
|
17
|
+
end
|
18
|
+
|
19
|
+
query_str
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: This will be expanded to calls other than /_view.
|
23
|
+
def get_fields(type)
|
24
|
+
if type == "view"
|
25
|
+
return [:reduce,:include_docs,:descending,:endkey,:endkey_docid,:group,:group_level,:inclusive_end,:key,:keys,:limit,:skip,:stale,:startkey,:startkey_docid]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cloudant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Yanai
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dotenv
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rest-client
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
description: A ruby interface to Cloudant's API.
|
98
|
+
email:
|
99
|
+
- yanai.alex@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/console
|
112
|
+
- bin/setup
|
113
|
+
- cloudant.gemspec
|
114
|
+
- lib/cloudant.rb
|
115
|
+
- lib/cloudant/client.rb
|
116
|
+
- lib/cloudant/connection.rb
|
117
|
+
- lib/cloudant/query_builder.rb
|
118
|
+
- lib/cloudant/version.rb
|
119
|
+
homepage: https://github.com/AlexYanai/cloudant
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.5.1
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Ruby interface to Cloudant's API.
|
143
|
+
test_files: []
|