sheetsu-ruby 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 +11 -0
- data/.rspec +1 -0
- data/.simplecov +3 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +245 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sheetsu.rb +44 -0
- data/lib/sheetsu/create.rb +24 -0
- data/lib/sheetsu/delete.rb +18 -0
- data/lib/sheetsu/errors.rb +25 -0
- data/lib/sheetsu/read.rb +18 -0
- data/lib/sheetsu/request.rb +109 -0
- data/lib/sheetsu/update.rb +25 -0
- data/lib/sheetsu/util.rb +53 -0
- data/lib/sheetsu/version.rb +3 -0
- data/sheetsu-ruby.gemspec +29 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 08990261ab0bd01d5d09dd026ece5ff594d28acc
|
4
|
+
data.tar.gz: 8025b4063f7f53881256b96df819e2e605ad1e32
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 86efae15d670ef5e58f8815ac00a75946aa9ed4a699ac39323166b7882f929829c3cd7315c54158030067ab5b6e1de1a6608c2b38fddd76f99e656582c11a0cd
|
7
|
+
data.tar.gz: 4380e107a9e11912db44c1c35fbfdea924726eb79111b4870a38d1cd405cc83eea70afc7a19397b05d884c2cb2cb95d086d4d35e426fc5a3affe3d90d0ea0a1d
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.simplecov
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at jb41.mail@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
# sheetsu-ruby
|
2
|
+
Ruby bindings for the Sheetsu API (https://sheetsu.com/docs).
|
3
|
+
|
4
|
+
## Installation
|
5
|
+
|
6
|
+
Add this line to your application's Gemfile:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'sheetsu-ruby'
|
10
|
+
```
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install sheetsu-ruby
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Generating a Client
|
23
|
+
|
24
|
+
You need to create a new Sheetsu::Client object, and populate it with your Sheetsu API URL. You can find this URL on [Sheetsu Dashboard](https://sheetsu.com/your-apis).
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# Create new client object
|
28
|
+
client = Sheetsu::Client.new("https://sheetsu.com/apis/v1.0/020b2c0f")
|
29
|
+
```
|
30
|
+
or
|
31
|
+
```ruby
|
32
|
+
# Create new client object
|
33
|
+
client = Sheetsu::Client.new("020b2c0f")
|
34
|
+
```
|
35
|
+
|
36
|
+
If you have HTTP Basic Authentication turned on for your API, you should pass `api_key` and `api_secret` here, like:
|
37
|
+
```ruby
|
38
|
+
# Create new client object with HTTP Basic Auth keys
|
39
|
+
client = Sheetsu::Client.new("020b2c0f", api_key: "YOUR_API_KEY", api_secret: "YOUR_API_SECRET")
|
40
|
+
```
|
41
|
+
|
42
|
+
### CRUD
|
43
|
+
|
44
|
+
Sheetsu gives you the ability to use full CRUD on your Google Spreadsheet. Remember to populate the first row of every sheet with column names. You can look at [example spreadsheet](https://docs.google.com/spreadsheets/d/1WTwXrh2ZDXmXATZlQIuapdv4ldyhJGZg7LX8GlzPdZw/edit?usp=sharing).
|
45
|
+
|
46
|
+
### Create
|
47
|
+
[Link to docs](https://sheetsu.com/docs#post)
|
48
|
+
|
49
|
+
To add data to Google Spreadsheets, send a hash or an array of hashes.
|
50
|
+
```ruby
|
51
|
+
# Adds single row
|
52
|
+
client.create({ id: 7, name: "Glenn", score: "69" })
|
53
|
+
|
54
|
+
# Adds bunch of rows
|
55
|
+
rows = [
|
56
|
+
{ id: 7, name: "Glenn", score: "69" },
|
57
|
+
{ id: 8, name: "Brian", score: "77" },
|
58
|
+
{ id: 9, name: "Joe", score: "45" }
|
59
|
+
]
|
60
|
+
client.create(rows)
|
61
|
+
```
|
62
|
+
|
63
|
+
By default, all writes are performed on the first sheet (worksheet). Pass name of a sheet as a 2nd param to add data to other worksheet.
|
64
|
+
```ruby
|
65
|
+
# Adds single row to worksheet named "Sheet3"
|
66
|
+
client.create({ "foo" => "bar", "baz" => "quux" }, "Sheet3")
|
67
|
+
```
|
68
|
+
|
69
|
+
On success returns a hash or an array of hashes with created values. On error check [errors](#errors).
|
70
|
+
|
71
|
+
### Read
|
72
|
+
[Link to docs](https://sheetsu.com/docs#get)
|
73
|
+
|
74
|
+
Read the whole sheet
|
75
|
+
```ruby
|
76
|
+
client.read
|
77
|
+
```
|
78
|
+
|
79
|
+
You can pass hash with options
|
80
|
+
- `sheet` - get data from named worksheet
|
81
|
+
- `limit` - limit number of results
|
82
|
+
- `offset` - start from N first record
|
83
|
+
- `search` - hash with search params [(more below)](#search)
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# Get first two rows from worksheet named "Sheet2"
|
87
|
+
client.read(sheet: "Sheet2", limit: 2)
|
88
|
+
|
89
|
+
# Get 5th record from worksheet named "Sheet3"
|
90
|
+
client.read(
|
91
|
+
offset: 4, # because rows are numbered from 0
|
92
|
+
limit: 1, # because only one row
|
93
|
+
sheet: "Sheet3"
|
94
|
+
)
|
95
|
+
```
|
96
|
+
|
97
|
+
#### search
|
98
|
+
[Link to docs](https://sheetsu.com/docs#get_search)
|
99
|
+
|
100
|
+
To get rows that match search criteria, pass a hash with search params
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# Get all rows where column 'id' is 'foo' and column 'value' is 'bar'
|
104
|
+
client.read(search: { id: "foo", value: "bar" })
|
105
|
+
|
106
|
+
# Get all rows where column 'First name' is 'Peter' and column 'Score' is '42'
|
107
|
+
client.read(search: { "First name" => "Peter", "Score" => 42 })
|
108
|
+
|
109
|
+
# Get first two row where column 'First name' is 'Peter',
|
110
|
+
# column 'Score' is '42' from sheet named "Sheet3"
|
111
|
+
client.read(
|
112
|
+
search: { "First name" => "Peter", "Score" => 42 } # search criteria
|
113
|
+
limit: 2 # first two rows
|
114
|
+
sheet: "Sheet3" # Sheet name
|
115
|
+
)
|
116
|
+
```
|
117
|
+
|
118
|
+
On success returns an array of hashes. On error check [errors](#errors).
|
119
|
+
|
120
|
+
### Update
|
121
|
+
[Link to docs](https://sheetsu.com/docs#patch)
|
122
|
+
|
123
|
+
To update row(s), pass column name and its value which is used to find row(s).
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# Update all columns where 'name' is 'Peter' to have 'score' = 99 and 'last name' = 'Griffin'
|
127
|
+
client.update(
|
128
|
+
"name", # column name
|
129
|
+
"Peter", # value to search for
|
130
|
+
{ "score": 99, "last name" => "Griffin" }, # hash with updates
|
131
|
+
)
|
132
|
+
```
|
133
|
+
|
134
|
+
By default, [PATCH request](https://sheetsu.com/docs#patch) is sent, which is updating only values which are in the hash passed to the method. To send [PUT request](https://sheetsu.com/docs#put), pass 4th argument being `true`. [Read more about the difference between PUT and PATCH in our docs](https://sheetsu.com/docs#patch).
|
135
|
+
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# Update all columns where 'name' is 'Peter' to have 'score' = 99 and 'last name' = 'Griffin'
|
139
|
+
# Empty all cells which matching, which are not 'score' or 'last name'
|
140
|
+
client.update(
|
141
|
+
"name", # column name
|
142
|
+
"Peter", # value to search for
|
143
|
+
{ "score": 99, "last name" => "Griffin" }, # hash with updates
|
144
|
+
true # nullify all fields not passed in the hash above
|
145
|
+
)
|
146
|
+
```
|
147
|
+
|
148
|
+
To perform `#update` on different than the first sheet, pass sheet name as a 5th argument.
|
149
|
+
```ruby
|
150
|
+
# Update all columns where 'name' is 'Peter' to have 'score' = 99 and 'last name' = 'Griffin'
|
151
|
+
# In sheet named 'Sheet3'
|
152
|
+
# Empty all cells which matching, which are not 'score' or 'last name'
|
153
|
+
client.update(
|
154
|
+
"name", # column name
|
155
|
+
"Peter", # value to search for
|
156
|
+
{ "score": 99, "last name" => "Griffin" }, # hash with updates
|
157
|
+
true, # nullify all fields not passed in the hash above
|
158
|
+
"Sheet3"
|
159
|
+
)
|
160
|
+
```
|
161
|
+
|
162
|
+
On success returns an array of hashes with updated values. On error check [errors](#errors).
|
163
|
+
|
164
|
+
### Delete
|
165
|
+
[Link to docs](https://sheetsu.com/docs#delete)
|
166
|
+
|
167
|
+
To delete row(s), pass column name and its value which is used to find row(s).
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
# Delete all rows where 'name' equals 'Peter'
|
171
|
+
client.delete(
|
172
|
+
"name", # column name
|
173
|
+
"Peter" # value to search for
|
174
|
+
)
|
175
|
+
```
|
176
|
+
|
177
|
+
You can pass sheet name as a 3rd argument. All operations are performed on the first sheet, by default.
|
178
|
+
```ruby
|
179
|
+
# Delete all rows where 'foo' equals 'bar' in sheet 'Sheet3'
|
180
|
+
client.delete(
|
181
|
+
"foo", # column name
|
182
|
+
"bar", # value to search for
|
183
|
+
"Sheet3" # sheet name
|
184
|
+
)
|
185
|
+
```
|
186
|
+
|
187
|
+
If success returns `:ok` symbol. If error check [errors](#errors).
|
188
|
+
|
189
|
+
### Errors
|
190
|
+
There are different styles of error handling. We choose to throw exceptions and signal failure loudly. You do not need to deal with any HTTP responses from the API calls directly. All exceptions are matching particular response code from Sheetsu API. You can [read more about it here](https://sheetsu.com/docs#statuses).
|
191
|
+
|
192
|
+
All exceptions are a subclass of `Sheetsu::SheetsuError`. The list of different error subclasses is listed below. You can choose to rescue each of them or rescue just the parent class (`Sheetsu::SheetsuError`).
|
193
|
+
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
Sheetsu::NotFoundError
|
197
|
+
Sheetsu::ForbiddenError
|
198
|
+
Sheetsu::LimitExceedError
|
199
|
+
Sheetsu::UnauthorizedError
|
200
|
+
```
|
201
|
+
|
202
|
+
## Development
|
203
|
+
|
204
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
205
|
+
|
206
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
207
|
+
|
208
|
+
Run all tests:
|
209
|
+
```
|
210
|
+
rspec
|
211
|
+
```
|
212
|
+
|
213
|
+
Run a single test:
|
214
|
+
```
|
215
|
+
rspec spec/read_spec.rb
|
216
|
+
```
|
217
|
+
|
218
|
+
## Contributing
|
219
|
+
|
220
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sheetsu/sheetsu-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
221
|
+
|
222
|
+
### Pull Requests
|
223
|
+
|
224
|
+
- **Add tests!** Your patch won't be accepted if it doesn't have tests.
|
225
|
+
|
226
|
+
- **Create topic branches**. Please, always create a branch with meaningful name. Don't ask us to pull from your master branch.
|
227
|
+
|
228
|
+
- **One pull request per feature**. If you want to do more than one thing, please send
|
229
|
+
multiple pull requests.
|
230
|
+
|
231
|
+
- **Send coherent history**. Make sure each individual commit in your pull
|
232
|
+
request is meaningful. If you had to make multiple intermediate commits while
|
233
|
+
developing, please squash them before sending them to us.
|
234
|
+
|
235
|
+
### Docs
|
236
|
+
|
237
|
+
[Sheetsu documentation sits on GitHub](https://github.com/sheetsu/docs). We would love your contributions! We want to make these docs accessible and easy to understand for everyone. Please send us Pull Requests or open issues on GitHub.
|
238
|
+
|
239
|
+
# To do
|
240
|
+
1. Allow passing whole API URLs as well as just slugs to the client, like:
|
241
|
+
```ruby
|
242
|
+
client = Sheetsu::Client.new("https://sheetsu.com/apis/v1.0/020b2c0f")
|
243
|
+
# should be the same as
|
244
|
+
client = Sheetsu::Client.new("020b2c0f")
|
245
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sheetsu"
|
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/lib/sheetsu.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'sheetsu/create'
|
2
|
+
require 'sheetsu/read'
|
3
|
+
require 'sheetsu/update'
|
4
|
+
require 'sheetsu/delete'
|
5
|
+
require 'sheetsu/util'
|
6
|
+
|
7
|
+
module Sheetsu
|
8
|
+
class Client
|
9
|
+
|
10
|
+
def initialize(api_url, auth_credentials={})
|
11
|
+
@api_url = Sheetsu::Util.parse_api_url(api_url)
|
12
|
+
@http_basic_auth = auth_credentials
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(data, sheet=nil)
|
16
|
+
if data.is_a?(Hash)
|
17
|
+
Sheetsu::Create.new(@api_url, @http_basic_auth).row(data, { sheet: sheet })
|
18
|
+
elsif data.is_a?(Array)
|
19
|
+
Sheetsu::Create.new(@api_url, @http_basic_auth).rows(data, { sheet: sheet })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def read(options={})
|
24
|
+
Sheetsu::Read.new(@api_url, @http_basic_auth).rows(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(column, value, data, update_whole=false, sheet=nil)
|
28
|
+
options = { column: column, value: value, data: data, update_whole: update_whole, sheet: sheet }
|
29
|
+
|
30
|
+
if update_whole
|
31
|
+
Sheetsu::Update.new(@api_url, @http_basic_auth).put(options)
|
32
|
+
else
|
33
|
+
Sheetsu::Update.new(@api_url, @http_basic_auth).patch(options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(column, value, sheet=nil)
|
38
|
+
options = { column: column, value: value, sheet: sheet }
|
39
|
+
|
40
|
+
Sheetsu::Delete.new(@api_url, @http_basic_auth).rows(options)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'sheetsu/util'
|
2
|
+
require 'sheetsu/errors'
|
3
|
+
require 'sheetsu/request'
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Sheetsu
|
8
|
+
class Create < Sheetsu::Request
|
9
|
+
|
10
|
+
def row(row, options={})
|
11
|
+
add_options_to_url(options)
|
12
|
+
|
13
|
+
response = call(:post, row)
|
14
|
+
parse_response(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rows(rows, options={})
|
18
|
+
add_options_to_url(options)
|
19
|
+
|
20
|
+
response = call(:post, { rows: rows })
|
21
|
+
parse_response(response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'sheetsu/util'
|
2
|
+
require 'sheetsu/errors'
|
3
|
+
require 'sheetsu/request'
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Sheetsu
|
8
|
+
class Delete < Sheetsu::Request
|
9
|
+
|
10
|
+
def rows(options)
|
11
|
+
add_options_to_url(options)
|
12
|
+
|
13
|
+
response = call(:delete)
|
14
|
+
parse_response(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sheetsu
|
2
|
+
class SheetsuError < StandardError
|
3
|
+
attr_reader :message
|
4
|
+
attr_reader :http_status
|
5
|
+
attr_reader :http_body
|
6
|
+
|
7
|
+
def initialize(message = nil, http_status = nil, http_body = nil)
|
8
|
+
@message = message
|
9
|
+
@http_status = http_status
|
10
|
+
@http_body = http_body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class NotFoundError < SheetsuError
|
15
|
+
end
|
16
|
+
|
17
|
+
class ForbiddenError < SheetsuError
|
18
|
+
end
|
19
|
+
|
20
|
+
class LimitExceedError < SheetsuError
|
21
|
+
end
|
22
|
+
|
23
|
+
class UnauthorizedError < SheetsuError
|
24
|
+
end
|
25
|
+
end
|
data/lib/sheetsu/read.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'sheetsu/util'
|
2
|
+
require 'sheetsu/errors'
|
3
|
+
require 'sheetsu/request'
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Sheetsu
|
8
|
+
class Read < Sheetsu::Request
|
9
|
+
|
10
|
+
def rows(options={})
|
11
|
+
add_options_to_url(options)
|
12
|
+
|
13
|
+
response = call(:get)
|
14
|
+
parse_response(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Sheetsu
|
4
|
+
class Request
|
5
|
+
|
6
|
+
def initialize(url, basic_auth)
|
7
|
+
@url = url
|
8
|
+
@basic_auth = basic_auth
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(method, body=nil)
|
12
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
13
|
+
http.use_ssl = true
|
14
|
+
|
15
|
+
request = request(method)
|
16
|
+
request = add_headers(request)
|
17
|
+
request = add_basic_auth(request)
|
18
|
+
request = add_body(request, body)
|
19
|
+
|
20
|
+
http.request(request)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def uri
|
26
|
+
@uri ||= URI.parse(@url)
|
27
|
+
end
|
28
|
+
|
29
|
+
def request(method)
|
30
|
+
request = http_klass(method).new(uri.request_uri)
|
31
|
+
add_headers(request)
|
32
|
+
|
33
|
+
request
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_headers(request)
|
37
|
+
Sheetsu::Util.default_headers.each_pair { |k,v| request[k] = v }
|
38
|
+
request
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_basic_auth(request)
|
42
|
+
if @basic_auth.keys.any?
|
43
|
+
request.basic_auth(CGI.unescape(@basic_auth[:api_key]), CGI.unescape(@basic_auth[:api_secret]))
|
44
|
+
end
|
45
|
+
request
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_body(request, body)
|
49
|
+
if body
|
50
|
+
request.body = body.to_json
|
51
|
+
end
|
52
|
+
request
|
53
|
+
end
|
54
|
+
|
55
|
+
def http_klass(method)
|
56
|
+
case method
|
57
|
+
when :get then Net::HTTP::Get
|
58
|
+
when :post then Net::HTTP::Post
|
59
|
+
when :put then Net::HTTP::Put
|
60
|
+
when :patch then Net::HTTP::Patch
|
61
|
+
when :delete then Net::HTTP::Delete
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_response(response)
|
66
|
+
Sheetsu::Util.parse_response(response)
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_options_to_url(options)
|
70
|
+
add_sheet_to_url(options)
|
71
|
+
add_column_value_to_url(options)
|
72
|
+
add_query_params_to_url(options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_column_value_to_url(options)
|
76
|
+
if options[:column] && options[:value]
|
77
|
+
@url += Sheetsu::Util.encoded_column(options)
|
78
|
+
|
79
|
+
options.delete(:column)
|
80
|
+
options.delete(:value)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_sheet_to_url(options)
|
85
|
+
if options[:sheet]
|
86
|
+
@url += ['/sheets/', CGI::escape(options[:sheet]).to_s].join('')
|
87
|
+
|
88
|
+
options.delete(:sheet)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_query_params_to_url(options)
|
93
|
+
h = Hash.new.tap do |hash|
|
94
|
+
hash[:limit] = options[:limit] if options[:limit]
|
95
|
+
hash[:offset] = options[:offset] if options[:offset]
|
96
|
+
|
97
|
+
if options[:search]
|
98
|
+
hash.merge!(options[:search])
|
99
|
+
@url += '/search'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
if h.keys.any?
|
104
|
+
@url = Sheetsu::Util.append_query_string_to_url(@url, h)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'sheetsu/util'
|
2
|
+
require 'sheetsu/errors'
|
3
|
+
require 'sheetsu/request'
|
4
|
+
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Sheetsu
|
8
|
+
class Update < Sheetsu::Request
|
9
|
+
|
10
|
+
def put(options)
|
11
|
+
add_options_to_url(options)
|
12
|
+
|
13
|
+
response = call(:put, options[:data])
|
14
|
+
parse_response(response)
|
15
|
+
end
|
16
|
+
|
17
|
+
def patch(options)
|
18
|
+
add_options_to_url(options)
|
19
|
+
|
20
|
+
response = call(:patch, options[:data])
|
21
|
+
parse_response(response)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/sheetsu/util.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
require 'sheetsu/version'
|
4
|
+
|
5
|
+
module Sheetsu
|
6
|
+
module Util
|
7
|
+
|
8
|
+
@sheetsu_api_url = "https://sheetsu.com/apis/v1.0/"
|
9
|
+
|
10
|
+
def self.default_headers
|
11
|
+
{
|
12
|
+
'Accept-Encoding' => 'gzip, deflate',
|
13
|
+
'Accept' => 'application/vnd.sheetsu.3+json',
|
14
|
+
'Content-Type' => 'application/json',
|
15
|
+
'User-Agent' => "Sheetsu-Ruby/#{Sheetsu::VERSION}"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_api_url(url)
|
20
|
+
[@sheetsu_api_url, url].join('')
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.append_query_string_to_url(url, options)
|
24
|
+
url + "?#{query_string(options)}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.encoded_column(options)
|
28
|
+
['/', CGI::escape(options[:column].to_s), '/', CGI::escape(options[:value].to_s)].join('')
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse_response(response)
|
32
|
+
case response.code.to_i
|
33
|
+
when 200 then JSON.parse(response.body)
|
34
|
+
when 201 then JSON.parse(response.body)
|
35
|
+
when 204 then :ok
|
36
|
+
when 401 then raise Sheetsu::UnauthorizedError
|
37
|
+
when 403 then raise Sheetsu::ForbiddenError
|
38
|
+
when 404 then raise Sheetsu::NotFoundError
|
39
|
+
when 429 then raise Sheetsu::LimitExceedError
|
40
|
+
else
|
41
|
+
raise Sheetsu::SheetsuError.new(nil, response.code, response.body)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def self.query_string(options)
|
47
|
+
options.map do |k,v|
|
48
|
+
"#{k.to_s}=#{CGI::escape(v.to_s)}"
|
49
|
+
end.join('&')
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sheetsu/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sheetsu-ruby"
|
8
|
+
spec.version = Sheetsu::VERSION
|
9
|
+
spec.authors = ["Michael Oblak"]
|
10
|
+
spec.email = ["m@sheetsu.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Turn Google Spreadsheets into REST APIs}
|
13
|
+
spec.description = %q{Sheetsu (https://sheetsu.com) allows you to automate Google Spreadsheets and use them as a resource in a REST API. It's easier than using standard Google Spreadsheets API, because Sheetsu handles all the 'dirty' work for you.}
|
14
|
+
spec.homepage = "https://sheetsu.com"
|
15
|
+
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.5"
|
26
|
+
spec.add_development_dependency "webmock", "~> 2.3"
|
27
|
+
spec.add_development_dependency "pry", "~> 0"
|
28
|
+
spec.add_development_dependency "simplecov", "~> 0"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sheetsu-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Oblak
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-09 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.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
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.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Sheetsu (https://sheetsu.com) allows you to automate Google Spreadsheets
|
98
|
+
and use them as a resource in a REST API. It's easier than using standard Google
|
99
|
+
Spreadsheets API, because Sheetsu handles all the 'dirty' work for you.
|
100
|
+
email:
|
101
|
+
- m@sheetsu.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".rspec"
|
108
|
+
- ".simplecov"
|
109
|
+
- CODE_OF_CONDUCT.md
|
110
|
+
- Gemfile
|
111
|
+
- README.md
|
112
|
+
- Rakefile
|
113
|
+
- bin/console
|
114
|
+
- bin/setup
|
115
|
+
- lib/sheetsu.rb
|
116
|
+
- lib/sheetsu/create.rb
|
117
|
+
- lib/sheetsu/delete.rb
|
118
|
+
- lib/sheetsu/errors.rb
|
119
|
+
- lib/sheetsu/read.rb
|
120
|
+
- lib/sheetsu/request.rb
|
121
|
+
- lib/sheetsu/update.rb
|
122
|
+
- lib/sheetsu/util.rb
|
123
|
+
- lib/sheetsu/version.rb
|
124
|
+
- sheetsu-ruby.gemspec
|
125
|
+
homepage: https://sheetsu.com
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.4.8
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Turn Google Spreadsheets into REST APIs
|
149
|
+
test_files: []
|
150
|
+
has_rdoc:
|