teamsnap_rb 0.0.8 → 1.0.0.beta1
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 +4 -4
- data/.travis.yml +13 -0
- data/Gemfile +2 -1
- data/LICENSE +1 -1
- data/README.md +31 -79
- data/Rakefile +19 -1
- data/lib/teamsnap/version.rb +3 -0
- data/lib/teamsnap.rb +270 -0
- data/spec/cassettes/apiv3-init.yml +1963 -0
- data/spec/cassettes/teamsnap_rb/adds_find_if_search_is_available.yml +56 -0
- data/spec/cassettes/teamsnap_rb/can_follow_plural_links.yml +111 -0
- data/spec/cassettes/teamsnap_rb/can_follow_singular_links.yml +105 -0
- data/spec/cassettes/teamsnap_rb/can_handle_an_empty_bulk_load.yml +52 -0
- data/spec/cassettes/teamsnap_rb/can_handle_an_error_with_bulk_load.yml +50 -0
- data/spec/cassettes/teamsnap_rb/can_handle_errors_generated_by_command.yml +50 -0
- data/spec/cassettes/teamsnap_rb/can_handle_links_with_no_data.yml +101 -0
- data/spec/cassettes/teamsnap_rb/can_use_bulk_load.yml +61 -0
- data/spec/cassettes/teamsnap_rb/handles_executing_an_action_via_commands.yml +54 -0
- data/spec/cassettes/teamsnap_rb/handles_fetching_data_via_queries.yml +56 -0
- data/spec/cassettes/teamsnap_rb/handles_queries_with_no_data.yml +53 -0
- data/spec/cassettes/teamsnap_rb/raises_an_exception_if_find_returns_nothing.yml +53 -0
- data/spec/spec_helper.rb +18 -13
- data/spec/teamsnap_spec.rb +125 -0
- data/teamsnap_rb.gemspec +17 -15
- metadata +100 -84
- data/lib/teamsnap_rb/client.rb +0 -8
- data/lib/teamsnap_rb/collection.rb +0 -179
- data/lib/teamsnap_rb/command.rb +0 -39
- data/lib/teamsnap_rb/commands.rb +0 -40
- data/lib/teamsnap_rb/config.rb +0 -14
- data/lib/teamsnap_rb/exceptions.rb +0 -3
- data/lib/teamsnap_rb/item.rb +0 -136
- data/lib/teamsnap_rb/link.rb +0 -24
- data/lib/teamsnap_rb/links_proxy.rb +0 -40
- data/lib/teamsnap_rb/models/event.rb +0 -4
- data/lib/teamsnap_rb/models/user.rb +0 -4
- data/lib/teamsnap_rb/queries_proxy.rb +0 -68
- data/lib/teamsnap_rb/request_builder.rb +0 -67
- data/lib/teamsnap_rb/template.rb +0 -37
- data/lib/teamsnap_rb/version.rb +0 -3
- data/lib/teamsnap_rb.rb +0 -22
- data/spec/client_spec.rb +0 -39
- data/spec/collection_spec.rb +0 -175
- data/spec/command_spec.rb +0 -60
- data/spec/commands_spec.rb +0 -35
- data/spec/config_spec.rb +0 -16
- data/spec/fixtures/cassettes/production_root.yml +0 -59
- data/spec/fixtures/cassettes/root.yml +0 -573
- data/spec/fixtures/cassettes/team.yml +0 -4858
- data/spec/fixtures/cassettes/teams.yml +0 -160
- data/spec/item_spec.rb +0 -133
- data/spec/link_spec.rb +0 -43
- data/spec/links_proxy_spec.rb +0 -32
- data/spec/queries_proxy_spec.rb +0 -31
- data/spec/template_spec.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ec7ccaac56d2dbb9377c7d191c93f1e95f72787
|
4
|
+
data.tar.gz: 3e99f2b6972f7a893678697b4f7e140040f39860
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6e8e9529786679bc1b332e8cc652156bb4455260c9236fdbcc6190a1f7bc5a9596ca5d829121ad6274b7778ac539a5b6c7ebc55b14470c883067f75654de711
|
7
|
+
data.tar.gz: c3d57d806335350eb1cd2d98bf3e491cdb2a6dd8b298fe0f3b8de48f1fb91689ccadc08d281e1b0c9a2682e3412ae5ffb29df284bb5dace4a8d6a444d90cf7ad
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,88 +1,40 @@
|
|
1
|
-
#
|
1
|
+
# teamsnap_rb
|
2
2
|
|
3
|
-
[
|
4
|
-
|
5
|
-
[](https://gemnasium.com/teamsnap/teamsnap_rb)
|
4
|
+
[](https://codeclimate.com/github/teamsnap/teamsnap_rb)
|
5
|
+
[](https://https://coveralls.io/r/teamsnap/teamsnap_rb)
|
6
|
+
[](https://travis-ci.org/teamsnap/teamsnap_rb)
|
7
|
+
[](https://rubygems.org/gems/teamsnap_rb)
|
8
|
+
[](http://opensource.org/licenses/MIT)
|
29
9
|
|
30
10
|
## Usage
|
31
11
|
|
32
|
-
|
33
|
-
|
34
|
-
## Contributing
|
35
|
-
|
36
|
-
### Git Workflow
|
37
|
-
|
38
|
-
1. Fork it ( http://github.com/teamsnap/teamsnap_rb/fork )
|
39
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
-
5. Create new Pull Request
|
43
|
-
|
44
|
-
### Testing teamsnap_rb
|
12
|
+
_Note: You'll need an OAuth2 Token from TeamSnap. Checkout our API docs
|
13
|
+
[here](http://developer.teamsnap.com/documentation/apiv3/)_
|
45
14
|
|
46
|
-
`./bin/rspec` will run the test suite.
|
47
|
-
|
48
|
-
### teamsnap_rb in IRB
|
49
|
-
|
50
|
-
```ruby
|
51
|
-
$ irb
|
52
|
-
> require 'teamsnap_rb'
|
53
|
-
=> true
|
54
|
-
> client_config = TeamsnapRb::Config.new(
|
55
|
-
> client_id: "your_client_id_here",
|
56
|
-
> client_secret: "ssshhhhhh!"
|
57
|
-
> )
|
58
|
-
> client = TeamsnapRb::Client.new("http://apiv3.teamsnap.com/", config: client_config)
|
59
|
-
=> # client object
|
60
|
-
> team = client.teams.search(:id => "your_team_id_here").first
|
61
|
-
=> # team object
|
62
15
|
```
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
>
|
70
|
-
|
71
|
-
|
72
|
-
>
|
73
|
-
|
74
|
-
>
|
75
|
-
=>
|
76
|
-
|
77
|
-
|
16
|
+
λ bundle install
|
17
|
+
Fetching gem metadata from https://rubygems.org/..........
|
18
|
+
...
|
19
|
+
Your bundle is complete!
|
20
|
+
Use `bundle show [gemname]` to see where a bundled gem is installed.
|
21
|
+
λ bundle exec rake console
|
22
|
+
[1] pry(main)> TeamSnap.init("abc123...", :url => "https://apiv3.teamsnap.com")
|
23
|
+
[2] pry(main)> t = TeamSnap::Team.find(1)
|
24
|
+
=> #<TeamSnap::Team::...>
|
25
|
+
[3] pry(main)> t.name
|
26
|
+
=> "TeamSnap"
|
27
|
+
[4] pry(main)> rs = client.bulk_load(:team_id => 1, :types => "team,member")
|
28
|
+
=> [#<TeamSnap::Team:...>,
|
29
|
+
#<TeamSnap::Member:...>,
|
30
|
+
# ...
|
31
|
+
#<TeamSnap::Member:...>]
|
32
|
+
[5] pry(main)> rs[1].first_name
|
33
|
+
=> "Andrew"
|
78
34
|
```
|
79
35
|
|
36
|
+
## Todo
|
80
37
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
authorize a client application using our [Cogsworth
|
85
|
-
service](https://auth.teamsnap.com).
|
86
|
-
|
87
|
-
With a client ID and secret in hand, you can begin interacting with the gem
|
88
|
-
using real data.
|
38
|
+
- Literate style docs?
|
39
|
+
- Cache items with threadsafe Hash (https://github.com/headius/thread_safe).
|
40
|
+
- Implement create, update and delete.
|
data/Rakefile
CHANGED
@@ -1,4 +1,22 @@
|
|
1
|
+
require "rubygems"
|
1
2
|
require "bundler/gem_tasks"
|
2
|
-
require
|
3
|
+
require "rake/clean"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
require "coveralls/rake/task"
|
6
|
+
|
7
|
+
CLOBBER.include("coverage")
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
|
+
t.fail_on_error = false
|
11
|
+
end
|
12
|
+
task :default => :spec
|
3
13
|
|
4
14
|
Coveralls::RakeTask.new
|
15
|
+
|
16
|
+
desc "start an IRB session with teamsnap loaded"
|
17
|
+
task :console do
|
18
|
+
require "pry"
|
19
|
+
require "./lib/teamsnap"
|
20
|
+
ARGV.clear
|
21
|
+
Pry.start
|
22
|
+
end
|
data/lib/teamsnap.rb
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "typhoeus"
|
3
|
+
require "typhoeus/adapters/faraday"
|
4
|
+
require "oj"
|
5
|
+
require "inflecto"
|
6
|
+
require "virtus"
|
7
|
+
require "date"
|
8
|
+
|
9
|
+
require_relative "teamsnap/version"
|
10
|
+
|
11
|
+
Oj.default_options = {
|
12
|
+
:mode => :compat,
|
13
|
+
:symbol_keys => true,
|
14
|
+
:class_cache => true
|
15
|
+
}
|
16
|
+
|
17
|
+
Faraday::Request.register_middleware(
|
18
|
+
:teamsnap_auth_middleware => -> { TeamSnap::AuthMiddleware }
|
19
|
+
)
|
20
|
+
|
21
|
+
Inflecto.inflections do |inflect|
|
22
|
+
inflect.irregular "member_preferences", "members_preferences"
|
23
|
+
inflect.irregular "opponent_results", "opponents_results"
|
24
|
+
inflect.irregular "team_preferences", "teams_preferences"
|
25
|
+
inflect.irregular "team_results", "teams_results"
|
26
|
+
end
|
27
|
+
|
28
|
+
module TeamSnap
|
29
|
+
EXCLUDED_RELS = ["me", "apiv2_root", "root", "self"]
|
30
|
+
DEFAULT_URL = "http://localhost:3000"
|
31
|
+
Error = Class.new(StandardError)
|
32
|
+
NotFound = Class.new(TeamSnap::Error)
|
33
|
+
|
34
|
+
class AuthMiddleware < Faraday::Middleware
|
35
|
+
def initialize(app, options)
|
36
|
+
super(app)
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(env)
|
41
|
+
token = @options.fetch(:token)
|
42
|
+
env[:request_headers].merge!({"Authorization" => "Bearer #{token}"})
|
43
|
+
|
44
|
+
@app.call(env)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def collection(href, resp)
|
50
|
+
Module.new do
|
51
|
+
define_singleton_method(:included) do |descendant|
|
52
|
+
descendant.send(:include, TeamSnap::Item)
|
53
|
+
descendant.extend(TeamSnap::Collection)
|
54
|
+
descendant.instance_variable_set(:@href, href)
|
55
|
+
descendant.instance_variable_set(:@resp, resp)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def hashify(arr)
|
61
|
+
arr.to_h
|
62
|
+
rescue NoMethodError
|
63
|
+
arr.inject({}) { |hash, (key, value)| hash[key] = value; hash }
|
64
|
+
end
|
65
|
+
|
66
|
+
def init(opts = {})
|
67
|
+
opts[:url] ||= DEFAULT_URL
|
68
|
+
opts.fetch(:token) {
|
69
|
+
raise ArgumentError.new("You must provide a :token to '.init'")
|
70
|
+
}
|
71
|
+
|
72
|
+
self.client = Faraday.new(
|
73
|
+
:url => opts.fetch(:url),
|
74
|
+
:parallel_manager => Typhoeus::Hydra.new
|
75
|
+
) do |c|
|
76
|
+
c.request :teamsnap_auth_middleware, {:token => opts[:token]}
|
77
|
+
c.adapter :typhoeus
|
78
|
+
end
|
79
|
+
|
80
|
+
collection = TeamSnap.run(:get, "/")
|
81
|
+
|
82
|
+
classes = []
|
83
|
+
client.in_parallel do
|
84
|
+
classes = collection
|
85
|
+
.fetch(:links)
|
86
|
+
.map { |link| classify_rel(link) }
|
87
|
+
.compact
|
88
|
+
end
|
89
|
+
|
90
|
+
classes.each { |cls| cls.parse_collection }
|
91
|
+
|
92
|
+
apply_endpoints(self, collection) && true
|
93
|
+
end
|
94
|
+
|
95
|
+
def run(via, href, args = {})
|
96
|
+
resp = client.send(via, href, args)
|
97
|
+
|
98
|
+
if resp.status == 200
|
99
|
+
Oj.load(resp.body)
|
100
|
+
.fetch(:collection)
|
101
|
+
else
|
102
|
+
error_message = parse_error(resp)
|
103
|
+
raise TeamSnap::Error.new(error_message)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def parse_error(resp)
|
108
|
+
Oj.load(resp.body)
|
109
|
+
.fetch(:collection)
|
110
|
+
.fetch(:error)
|
111
|
+
.fetch(:message)
|
112
|
+
end
|
113
|
+
|
114
|
+
def load_items(collection)
|
115
|
+
collection
|
116
|
+
.fetch(:items) { [] }
|
117
|
+
.map { |item|
|
118
|
+
data = parse_data(item)
|
119
|
+
type = type_of(item)
|
120
|
+
cls = load_class(type, data)
|
121
|
+
|
122
|
+
cls.new(data).tap { |obj|
|
123
|
+
obj.send(:load_links, item.fetch(:links))
|
124
|
+
}
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def apply_endpoints(obj, collection)
|
129
|
+
collection
|
130
|
+
.fetch(:queries) { [] }
|
131
|
+
.each { |endpoint| register_endpoint(obj, endpoint, :via => :get) }
|
132
|
+
|
133
|
+
collection
|
134
|
+
.fetch(:commands) { [] }
|
135
|
+
.each { |endpoint| register_endpoint(obj, endpoint, :via => :post) }
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
attr_accessor :client
|
141
|
+
|
142
|
+
def classify_rel(link)
|
143
|
+
return if EXCLUDED_RELS.include?(link.fetch(:rel))
|
144
|
+
|
145
|
+
rel = link.fetch(:rel)
|
146
|
+
href = link.fetch(:href)
|
147
|
+
name = Inflecto.classify(rel)
|
148
|
+
resp = client.get(href)
|
149
|
+
|
150
|
+
TeamSnap.const_set(
|
151
|
+
name, Class.new { include TeamSnap.collection(href, resp) }
|
152
|
+
) unless TeamSnap.const_defined?(name)
|
153
|
+
end
|
154
|
+
|
155
|
+
def register_endpoint(obj, endpoint, opts)
|
156
|
+
rel = endpoint.fetch(:rel)
|
157
|
+
href = endpoint.fetch(:href)
|
158
|
+
valid_args = endpoint.fetch(:data) { [] }
|
159
|
+
.map { |datum| datum.fetch(:name).to_sym }
|
160
|
+
via = opts.fetch(:via)
|
161
|
+
|
162
|
+
obj.define_singleton_method(rel) do |*args|
|
163
|
+
args = Hash[*args]
|
164
|
+
|
165
|
+
unless args.all? { |arg, _| valid_args.include?(arg) }
|
166
|
+
raise ArgumentError.new(
|
167
|
+
"Invalid argument(s). Valid argument(s) are #{valid_args.inspect}"
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
TeamSnap.load_items(
|
172
|
+
TeamSnap.run(via, href, args)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_data(item)
|
178
|
+
data = item
|
179
|
+
.fetch(:data)
|
180
|
+
.map { |datum|
|
181
|
+
name = datum.fetch(:name)
|
182
|
+
value = datum.fetch(:value)
|
183
|
+
type = datum.fetch(:type) { :default }
|
184
|
+
|
185
|
+
value = DateTime.parse(value) if value && type == "DateTime"
|
186
|
+
|
187
|
+
[name, value]
|
188
|
+
}
|
189
|
+
TeamSnap.hashify(data)
|
190
|
+
end
|
191
|
+
|
192
|
+
def type_of(item)
|
193
|
+
item
|
194
|
+
.fetch(:data)
|
195
|
+
.find { |datum| datum.fetch(:name) == "type" }
|
196
|
+
.fetch(:value)
|
197
|
+
end
|
198
|
+
|
199
|
+
def load_class(type, data)
|
200
|
+
TeamSnap.const_get(Inflecto.classify(type)).tap { |cls|
|
201
|
+
unless cls.include?(Virtus::Model::Core)
|
202
|
+
cls.class_eval do
|
203
|
+
include Virtus.value_object
|
204
|
+
|
205
|
+
values do
|
206
|
+
attribute :href, String
|
207
|
+
data.each { |name, value| attribute name, value.class }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
}
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
module Item
|
216
|
+
private
|
217
|
+
|
218
|
+
def load_links(links)
|
219
|
+
links.each do |link|
|
220
|
+
next if EXCLUDED_RELS.include?(link.fetch(:rel))
|
221
|
+
|
222
|
+
rel = link.fetch(:rel)
|
223
|
+
href = link.fetch(:href)
|
224
|
+
is_singular = rel == Inflecto.singularize(rel)
|
225
|
+
|
226
|
+
define_singleton_method(rel) {
|
227
|
+
coll = TeamSnap.load_items(
|
228
|
+
TeamSnap.run(:get, href)
|
229
|
+
)
|
230
|
+
coll.size == 1 && is_singular ? coll.first : coll
|
231
|
+
}
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
module Collection
|
237
|
+
def href
|
238
|
+
self.instance_variable_get(:@href)
|
239
|
+
end
|
240
|
+
|
241
|
+
def resp
|
242
|
+
self.instance_variable_get(:@resp)
|
243
|
+
end
|
244
|
+
|
245
|
+
def parse_collection
|
246
|
+
if resp.status == 200
|
247
|
+
collection = Oj.load(resp.body)
|
248
|
+
.fetch(:collection)
|
249
|
+
|
250
|
+
TeamSnap.apply_endpoints(self, collection)
|
251
|
+
enable_find if respond_to?(:search)
|
252
|
+
else
|
253
|
+
error_message = TeamSnap.parse_error(resp)
|
254
|
+
raise TeamSnap::Error.new(error_message)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
def enable_find
|
261
|
+
define_singleton_method(:find) do |id|
|
262
|
+
search(:id => id).first.tap do |object|
|
263
|
+
raise TeamSnap::NotFound.new(
|
264
|
+
"Could not find a #{self} with an id of '#{id}'."
|
265
|
+
) unless object
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|