teamsnap_rb 1.3.3 → 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +131 -29
  4. data/lib/config/inflecto.rb +13 -0
  5. data/lib/config/oj.rb +5 -0
  6. data/lib/teamsnap.rb +36 -376
  7. data/lib/teamsnap/api.rb +113 -0
  8. data/lib/teamsnap/auth_middleware.rb +62 -0
  9. data/lib/teamsnap/client.rb +51 -0
  10. data/lib/teamsnap/collection.rb +125 -0
  11. data/lib/teamsnap/item.rb +100 -0
  12. data/lib/teamsnap/response.rb +101 -0
  13. data/lib/teamsnap/structure.rb +80 -0
  14. data/lib/teamsnap/version.rb +1 -1
  15. data/spec/cassettes/apiv3-init.yml +756 -124
  16. data/spec/cassettes/client/when_calling_via_s_on_the_client/does_not_raise_an_error_when_the_HTTP_actions_are_called.yml +56 -0
  17. data/spec/cassettes/structure/_create_collection_class/registers_new_classes_via_introspection_of_the_root_collection.yml +110 -0
  18. data/spec/cassettes/structure/_create_collection_class/sets_the_href_attribute_on_the_new_class.yml +57 -0
  19. data/spec/cassettes/structure/_init/has_all_classes_in_schema_loaded_except_for_exceptions_list_endpoints.yml +56 -0
  20. data/spec/cassettes/structure/has_all_classes_in_schema_loaded_except_for_exceptions_list_endpoints.yml +56 -0
  21. data/spec/cassettes/teamsnap__client/when_calling_via_s_on_the_client/does_not_raise_an_error_when_the_HTTP_actions_are_called.yml +69 -0
  22. data/spec/cassettes/teamsnap__client/when_calling_via_s_on_the_client/passes_them_to_the_faraday_client_using_method_missing.yml +69 -0
  23. data/spec/cassettes/teamsnap__collection/adds_find_if_search_is_available.yml +61 -0
  24. data/spec/cassettes/teamsnap__collection/adds_href_to_items.yml +62 -0
  25. data/spec/cassettes/teamsnap__collection/can_follow_plural_links.yml +179 -0
  26. data/spec/cassettes/teamsnap__collection/can_follow_singular_links.yml +120 -0
  27. data/spec/cassettes/teamsnap__collection/can_handle_links_with_no_data.yml +107 -0
  28. data/spec/cassettes/{teamsnap_rb/can_handle_errors_generated_by_command.yml → teamsnap__collection/can_handle_no_argument_errors_generated_by_command.yml} +10 -16
  29. data/spec/cassettes/teamsnap__collection/handles_executing_an_action_via_commands.yml +64 -0
  30. data/spec/cassettes/teamsnap__collection/handles_executing_an_action_via_commands_with_multiple_params.yml +70 -0
  31. data/spec/cassettes/teamsnap__collection/handles_fetching_data_via_queries.yml +61 -0
  32. data/spec/cassettes/teamsnap__collection/handles_queries_with_no_data.yml +57 -0
  33. data/spec/cassettes/teamsnap__collection/raises_an_exception_if_find_returns_nothing.yml +57 -0
  34. data/spec/cassettes/teamsnap__collection/supports_relations_with_expected_behaviors/when_a_plural_relation_is_called/responds_with_an_array_of_objects_when_successful.yml +117 -0
  35. data/spec/cassettes/teamsnap__collection/supports_relations_with_expected_behaviors/when_a_plural_relation_is_called/responds_with_an_empty_array_when_no_objects_exist.yml +111 -0
  36. data/spec/cassettes/teamsnap__collection/supports_relations_with_expected_behaviors/when_a_singular_relation_is_called/responds_with_nil_if_it_does_NOT_exist.yml +111 -0
  37. data/spec/cassettes/teamsnap__collection/supports_relations_with_expected_behaviors/when_a_singular_relation_is_called/responds_with_the_object_if_it_exists.yml +124 -0
  38. data/spec/cassettes/teamsnap__structure/_create_collection_class/registers_new_classes_via_introspection_of_the_root_collection.yml +57 -0
  39. data/spec/cassettes/teamsnap__structure/_create_collection_class/sets_the_href_attribute_on_the_new_class.yml +57 -0
  40. data/spec/cassettes/teamsnap__structure/_init/has_all_classes_in_schema_loaded_except_for_exceptions_list_endpoints.yml +69 -0
  41. data/spec/cassettes/teamsnap_rb/_bulk_load/can_handle_an_empty_bulk_load.yml +55 -0
  42. data/spec/cassettes/teamsnap_rb/{can_handle_an_error_with_bulk_load.yml → _bulk_load/can_handle_an_error_with_bulk_load.yml} +11 -23
  43. data/spec/cassettes/teamsnap_rb/_bulk_load/can_use_bulk_load.yml +121 -0
  44. data/spec/cassettes/teamsnap_rb/_client_send/when_sent_a_known_via_/calls_DELETE_on_the_given_client.yml +2405 -0
  45. data/spec/cassettes/teamsnap_rb/_client_send/when_sent_a_known_via_/calls_GET_on_the_given_client.yml +69 -0
  46. data/spec/cassettes/teamsnap_rb/_client_send/when_sent_a_known_via_/calls_PATCH_on_the_given_client.yml +2404 -0
  47. data/spec/cassettes/teamsnap_rb/_client_send/when_sent_a_known_via_/calls_POST_on_the_given_client.yml +2404 -0
  48. data/spec/cassettes/teamsnap_rb/_run/processes_the_response.yml +267 -0
  49. data/spec/cassettes/teamsnap_rb/adds_find_if_search_is_available.yml +27 -30
  50. data/spec/cassettes/teamsnap_rb/adds_href_to_items.yml +31 -19
  51. data/spec/cassettes/teamsnap_rb/can_follow_plural_links.yml +118 -67
  52. data/spec/cassettes/teamsnap_rb/can_follow_singular_links.yml +59 -56
  53. data/spec/cassettes/teamsnap_rb/can_handle_links_with_no_data.yml +48 -38
  54. data/spec/cassettes/teamsnap_rb/can_handle_no_argument_errors_generated_by_command.yml +42 -0
  55. data/spec/cassettes/teamsnap_rb/handles_executing_an_action_via_commands.yml +32 -20
  56. data/spec/cassettes/teamsnap_rb/handles_executing_an_action_via_commands_with_multiple_params.yml +29 -404
  57. data/spec/cassettes/teamsnap_rb/handles_fetching_data_via_queries.yml +26 -23
  58. data/spec/cassettes/teamsnap_rb/handles_queries_with_no_data.yml +24 -28
  59. data/spec/cassettes/teamsnap_rb/raises_an_exception_if_find_returns_nothing.yml +24 -28
  60. data/spec/cassettes/teamsnap_rb/supports_relations_with_expected_behaviors/when_a_plural_relation_is_called/responds_with_an_array_of_objects_when_successful.yml +53 -45
  61. data/spec/cassettes/teamsnap_rb/supports_relations_with_expected_behaviors/when_a_plural_relation_is_called/responds_with_an_empty_array_when_no_objects_exist.yml +50 -54
  62. data/spec/cassettes/teamsnap_rb/supports_relations_with_expected_behaviors/when_a_singular_relation_is_called/responds_with_nil_if_it_does_NOT_exist.yml +50 -54
  63. data/spec/cassettes/teamsnap_rb/supports_relations_with_expected_behaviors/when_a_singular_relation_is_called/responds_with_the_object_if_it_exists.yml +59 -58
  64. data/spec/spec_helper.rb +2 -0
  65. data/spec/teamsnap/client_spec.rb +75 -0
  66. data/spec/teamsnap/collection_spec.rb +155 -0
  67. data/spec/teamsnap/item_spec.rb +155 -0
  68. data/spec/teamsnap/structure_spec.rb +63 -0
  69. data/spec/teamsnap_spec.rb +169 -157
  70. data/teamsnap_rb.gemspec +1 -1
  71. metadata +92 -15
  72. data/spec/cassettes/teamsnap_rb/can_handle_an_empty_bulk_load.yml +0 -60
  73. data/spec/cassettes/teamsnap_rb/can_use_bulk_load.yml +0 -74
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0239f7246eecc56c771e8a38313131be8be7ee5
4
- data.tar.gz: 58a567e81b97aa5057131841d5330a3b09106392
3
+ metadata.gz: a93f427c06bd23b1c461e7972a5be914b48afa1c
4
+ data.tar.gz: b49a4d94aa26ee91aea101677ad74f3d3941b1bb
5
5
  SHA512:
6
- metadata.gz: 05a720ecb0301f7b8531e6dc7649b2d94ba405baa322bb12af48798fb921462cc3ccd26168a1f5a16be6c9b2d719198ca0decdfc4335fe3863a4f93833fa9aec
7
- data.tar.gz: d3f5ff84adf69cfe375efc974514af485c4515c9cf552df95e73090c4abda44e7378f42aa3a7014809c0460f6ad160b9b7600c634b19549291a6475e505aeed8
6
+ metadata.gz: 9485d5d81583191d1d4027d61e050b6e99f1312f732d045faa7a9951f4b1e5ba31a3536ea2cd9b757d0acc22fef45863e8a655944e9e8dd6bb3dae8b9d3d03e7
7
+ data.tar.gz: 18ba0a37191961fe69074e67f13a82dffb2ef208094423118c9eb15f6dc092d9f0f63d18f4c921139d311579cacb7c10d2ef75099ebcf4f08ebe2c6a315d3a82
@@ -14,3 +14,9 @@ v1.3.3
14
14
 
15
15
  - Have the ability to search for a /schemas endpoint and create the collections
16
16
  off of the schema endpoint instead of hitting each endpoint individually
17
+
18
+ v2.0.0.beta
19
+ ------
20
+
21
+ - Full CRUD actions available along with TeamSnap::Response option for better
22
+ control-flow statements.
data/README.md CHANGED
@@ -12,37 +12,139 @@
12
12
  _Note: You'll need an OAuth2 Token from TeamSnap. Checkout our API docs
13
13
  [here](http://developer.teamsnap.com/documentation/apiv3/)_
14
14
 
15
- ```
16
- λ gem install teamsnap_rb
17
- λ irb
18
- > TeamSnap.init(:token => "abc123...")
19
- > t = TeamSnap::Team.find(1)
20
- => #<TeamSnap::Team::...>
21
- > t.name
22
- => "TeamSnap"
23
- > rs = client.bulk_load(:team_id => 1, :types => "team,member")
24
- => [#<TeamSnap::Team:...>,
25
- #<TeamSnap::Member:...>,
26
- # ...
27
- #<TeamSnap::Member:...>]
28
- > rs[1].first_name
29
- => "Andrew"
30
- ```
31
-
32
- ## Backup Cache
33
-
34
- By default when calling TeamSnap.init(...), there is a backup cache of the API root response used
35
- to setup all relevant classes. This is primarily used so CI testing and deploy jobs do not have to
36
- connect to the API to complete.
37
-
38
- This creates a .teamsnap\_rb file in your ./tmp directory (./tmp/.teamsnap_rb). If you would like to turn this functionality
39
- off, just set ```:backup_cache => false``` in your init options. Alternatively, you can set the location
40
- of the backup by passing a string location of where to store the file. e.g.
41
- ```:backup_cache => "./another/location/.teamsnap_rb_file"```
15
+ λ gem install teamsnap_rb
16
+ λ irb
17
+ TeamSnap.init(:client_id => XXXXX, :client_secret => XXXXX)
18
+ => true
19
+
20
+ # Now you have your base connection to the API under:
21
+ TeamSnap.root_client
22
+ => #<TeamSnap::Client:...>
23
+
24
+ # The below imply usage of:
25
+ client = TeamSnap.root_client
26
+
27
+ t = TeamSnap::Team.find(client, 1)
28
+ => #<TeamSnap::Team::...>
29
+ t.name
30
+ => "TeamSnap"
31
+ rs = client.bulk_load(client, {:team_id => 1, :types => "team,member"})
32
+ => [
33
+ => #<TeamSnap::Team:...>,
34
+ => #<TeamSnap::Member:...>,
35
+ => # ...
36
+ => #<TeamSnap::Member:...>
37
+ => ]
38
+ rs[1].first_name
39
+ => "Andrew"
40
+
41
+
42
+ #########################
43
+ Class Syntax
44
+ #########################
45
+ - raises error on exception
46
+ - returns Object(TeamSnap::Class) / Objects(Array) as response
47
+
48
+ # find
49
+ team = TeamSnap::Team.find(client, XXX)
50
+
51
+ # create
52
+ team = TeamSnap::Team.create(client, {:attribute_name => value})
53
+
54
+ # update
55
+ team = TeamSnap::Team.update(client, {:id => XXX, :attribute_name => value})
56
+
57
+ # delete
58
+ no_team = TeamSnap::Team.delete(client, id)
59
+
60
+ # search
61
+ teams = TeamSnap::Team.search(client, {:filter_on => filter_value})
62
+
63
+ # command
64
+ team = TeamSnap::Team.command_name(client, {:attr => val})
65
+
66
+
67
+ #########################
68
+ Api Class Syntax
69
+ #########################
70
+ - returns TeamSnap::Response object with select methods
71
+ - optionally converts attributes hash into template: { data: {}}
72
+
73
+ # find
74
+ response = TeamSnap::Api.run(client, :find, :members, 1)
75
+ => #<TeamSnap::Response:...>
76
+ response.success?
77
+ => true
78
+ member = response.objects.first
79
+ => #<TeamSnap::Member:...>
80
+ response.errors?
81
+ => false
82
+ response.message
83
+ => "Data retrieved successfully"
84
+
85
+ # create
86
+ response = TeamSnap::Api.run(client, :create, TeamSnap::Member, {:attr_name => attr_value}, true)
87
+ => #<TeamSnap::Response:...>
88
+ response.success?
89
+ => false
90
+ response.errors?
91
+ => true
92
+ response.message
93
+ => "first_name can't be blank; team_id can't be blank"
94
+
95
+ # update
96
+ TeamSnap::Api.run(client, :update, :member, {}, true)
97
+ => #<TeamSnap::Response:...>
98
+
99
+ # delete
100
+ TeamSnap::Api.run(client, :delete, :members, id)
101
+ => #<TeamSnap::Response:...>
102
+
103
+ # search & other queries
104
+ TeamSnap::Api.run(client, :search, TeamSnap::Member, {:team_id => some_team_id})
105
+ => #<TeamSnap::Response:...>
106
+
107
+ # commands
108
+ TeamSnap::Api.run(client, :command, :member, {:optional_attr => value})
109
+ => #<TeamSnap::Response:...>
110
+
111
+
112
+ #########################
113
+ Client Object Calls
114
+ #########################
115
+ - returns TeamSnap::Response object with select methods
116
+ - automatically translates attributes to / from template: { data: {}}
117
+
118
+ # find
119
+ client.api(:find, :forum_posts, 1)
120
+ => #<TeamSnap::Response:...>
121
+
122
+ # search and other 'queries' (arguments patch 'as-is')
123
+ client.api(:search, TeamSnap::ForumPost, {:forum_topic_id => 2})
124
+ => #<TeamSnap::Response:...>
125
+
126
+ # create (arguments post in template data format)
127
+ client.api(:create, :forum_post, forum_post_success)
128
+ => #<TeamSnap::Response:...>
129
+
130
+ # update (arguments post in template data format)
131
+ client.api(:update, TeamSnap::ForumPost, forum_patch_success)
132
+ => #<TeamSnap::Response:...>
133
+
134
+ # delete
135
+ client.api(:delete, :forum_posts, 1)
136
+ => #<TeamSnap::Response:...>
137
+
138
+ # commands (arguments post 'as-is')
139
+ client.api(:command_name, :forum_post, {})
140
+ => #<TeamSnap::Response:...>
141
+
142
+ # queries (arguments sent in href key/value pairs)
143
+ client.api(:query_name, :members, {})
144
+ => #<TeamSnap::Response:...>
145
+
42
146
 
43
147
 
44
148
  ## Todo
45
149
 
46
- - Literate style docs?
47
150
  - Cache items with threadsafe Hash (https://github.com/headius/thread_safe).
48
- - Implement create, update and delete.
@@ -0,0 +1,13 @@
1
+ Inflecto.inflections do |inflect|
2
+ inflect.irregular "broadcast_sms", "broadcast_smses"
3
+ inflect.irregular "division_member_preferences", "division_members_preferences"
4
+ inflect.irregular "division_preferences", "divisions_preferences"
5
+ inflect.irregular "member_preferences", "member_preferences"
6
+ inflect.irregular "member_preferences", "members_preferences"
7
+ inflect.irregular "opponent_results", "opponent_results"
8
+ inflect.irregular "opponent_results", "opponents_results"
9
+ inflect.irregular "team_preferences", "team_preferences"
10
+ inflect.irregular "team_preferences", "teams_preferences"
11
+ inflect.irregular "team_results", "team_results"
12
+ inflect.irregular "team_results", "teams_results"
13
+ end
@@ -0,0 +1,5 @@
1
+ Oj.default_options = {
2
+ :mode => :compat,
3
+ :symbol_keys => true,
4
+ :class_cache => true
5
+ }
@@ -1,219 +1,61 @@
1
- require "faraday"
2
- require "typhoeus"
3
- require "typhoeus/adapters/faraday"
4
- require "oj"
5
- require "inflecto"
6
- require "virtus"
7
- require "date"
8
- require "securerandom"
1
+ %w(
2
+ faraday typhoeus typhoeus/adapters/faraday oj inflecto virtus
3
+ date securerandom
4
+ ).each { |x| require x }
9
5
 
10
- require_relative "teamsnap/version"
11
-
12
- Oj.default_options = {
13
- :mode => :compat,
14
- :symbol_keys => true,
15
- :class_cache => true
16
- }
17
-
18
- Faraday::Request.register_middleware(
19
- :teamsnap_auth_middleware => -> { TeamSnap::AuthMiddleware }
20
- )
21
-
22
- Inflecto.inflections do |inflect|
23
- inflect.irregular "broadcast_sms", "broadcast_smses"
24
- inflect.irregular "member_preferences", "member_preferences"
25
- inflect.irregular "member_preferences", "members_preferences"
26
- inflect.irregular "opponent_results", "opponent_results"
27
- inflect.irregular "opponent_results", "opponents_results"
28
- inflect.irregular "team_preferences", "team_preferences"
29
- inflect.irregular "team_preferences", "teams_preferences"
30
- inflect.irregular "team_results", "team_results"
31
- inflect.irregular "team_results", "teams_results"
32
- end
6
+ %w(
7
+ config/inflecto config/oj teamsnap/version teamsnap/api
8
+ teamsnap/auth_middleware teamsnap/client teamsnap/collection teamsnap/item
9
+ teamsnap/response teamsnap/structure
10
+ ).each { |x| require_relative x }
33
11
 
34
12
  module TeamSnap
35
- EXCLUDED_RELS = %w(me apiv2_root root self dude sweet random xyzzy schemas)
13
+ EXCLUDED_RELS = %w(me apiv2_root root self dude sweet random xyzzy schemas
14
+ authorization plans_all tsl_photos)
36
15
  DEFAULT_URL = "https://apiv3.teamsnap.com"
37
16
  Error = Class.new(StandardError)
38
17
  NotFound = Class.new(TeamSnap::Error)
39
18
 
40
- class AuthMiddleware < Faraday::Middleware
41
- def initialize(app, options)
42
- @options = options
43
- super(app)
44
- end
45
-
46
- def call(env)
47
- if token
48
- env[:request_headers].merge!({"Authorization" => "Bearer #{token}"})
49
- elsif client_id && client_secret
50
- query_params = Hash[URI.decode_www_form(env.url.query || "")]
51
- .merge({
52
- hmac_client_id: client_id,
53
- hmac_nonce: SecureRandom.uuid,
54
- hmac_timestamp: Time.now.to_i
55
- })
56
- env.url.query = URI.encode_www_form(query_params)
57
-
58
- env.request_headers["X-Teamsnap-Hmac"] = OpenSSL::HMAC.hexdigest(
59
- digest, client_secret, message_hash(env)
60
- )
61
- end
62
-
63
- @app.call(env)
64
- end
65
-
66
- def token
67
- @token ||= @options[:token]
68
- end
69
-
70
- def client_id
71
- @client_id ||= @options[:client_id]
72
- end
73
-
74
- def client_secret
75
- @client_secret ||= @options[:client_secret]
76
- end
77
-
78
- def digest
79
- OpenSSL::Digest.new("sha256")
80
- end
81
-
82
- def message_hash(env)
83
- digest.hexdigest(
84
- query_string(env) + message(env)
85
- )
86
- end
87
-
88
- def query_string(env)
89
- "/?" + env.url.query.to_s
90
- end
91
-
92
- def message(env)
93
- env.body || ""
94
- end
95
- end
96
-
97
19
  class << self
98
- def collection(href, resp, parsed_collection)
99
- Module.new do
100
- define_singleton_method(:included) do |descendant|
101
- descendant.send(:include, TeamSnap::Item)
102
- descendant.extend(TeamSnap::Collection)
103
- descendant.instance_variable_set(:@href, href)
104
- descendant.instance_variable_set(:@resp, resp)
105
- descendant.instance_variable_set(:@parsed_collection, parsed_collection)
106
- end
107
- end
108
- end
109
-
110
- def hashify(arr)
111
- arr.to_h
112
- rescue NoMethodError
113
- arr.inject({}) { |hash, (key, value)| hash[key] = value; hash }
114
- end
20
+ attr_accessor :client_id, :client_secret, :root_client, :token, :url
115
21
 
116
22
  def init(opts = {})
117
- opts[:url] ||= DEFAULT_URL
118
- unless opts.include?(:token) || (
119
- opts.include?(:client_id) && opts.include?(:client_secret)
120
- )
121
- raise ArgumentError.new(
122
- "You must provide a :token or :client_id and :client_secret pair to '.init'"
123
- )
124
- end
125
-
126
- self.client = Faraday.new(
127
- :url => opts.fetch(:url),
128
- :parallel_manager => Typhoeus::Hydra.new
129
- ) do |c|
130
- c.request :teamsnap_auth_middleware, {
131
- :token => opts[:token],
132
- :client_id => opts[:client_id],
133
- :client_secret => opts[:client_secret]
134
- }
135
- c.adapter :typhoeus
23
+ unless opts[:token] || (opts[:client_id] && opts[:client_secret])
24
+ raise ArgumentError.new("You must provide a :token or :client_id and :client_secret pair to '.init'")
136
25
  end
137
26
 
138
- collection = TeamSnap.run_init(:get, "/", {})
27
+ ## setup variables required
28
+ self.client_id = opts.fetch(:client_id) {}
29
+ self.client_secret = opts.fetch(:client_secret) {}
30
+ self.token = opts.fetch(:token) {}
31
+ self.url = opts.fetch(:url) { DEFAULT_URL }
139
32
 
140
- classes = []
33
+ ## create universally accessible TeamSnap.root_client
34
+ self.root_client = TeamSnap::Client.new(:token => token)
141
35
 
142
- schema = collection
143
- .fetch(:links) { [] }
144
- .find { |link| link[:rel] == "schemas" } || {}
145
-
146
- if schema[:href]
147
- href_to_rel = collection
148
- .fetch(:links) { [] }
149
- .reject { |link| EXCLUDED_RELS.include?(link[:rel]) }
150
- .map { |link| [link[:href], link[:rel]]}
151
- .to_h
152
-
153
- resp = client.get(schema[:href])
154
- if resp.status == 200
155
- collections = Oj.load(resp.body)
156
- classes = collections.map { |collection|
157
- col = collection.fetch(:collection) { {} }
158
- if rel = href_to_rel[col[:href]]
159
- create_collection_class(rel, col[:href], nil, col)
160
- end
161
- }
162
- .compact
163
- else
164
- error_message = TeamSnap.parse_error(resp)
165
- raise TeamSnap::Error.new(error_message)
166
- end
167
- else
168
- client.in_parallel do
169
- classes = collection
170
- .fetch(:links) { [] }
171
- .map { |link| classify_rel(link) }
172
- .compact
173
- end
174
- end
175
- classes.each { |cls| cls.parse_collection }
36
+ ## Make the apiv3 root call. collection is parsed JSON
37
+ collection = TeamSnap.run(root_client, :get, "/", {})
176
38
 
177
- apply_endpoints(self, collection) && true
178
- end
39
+ ## Setup Dynamic Classes from the collection
40
+ TeamSnap::Structure.init(root_client, collection)
179
41
 
180
- def run_init(via, href, args = {}, opts = {})
181
- begin
182
- resp = client_send(via, href, args)
183
- rescue Faraday::TimeoutError
184
- warn("Connection to API failed. Initializing with empty class structure")
185
- {:links => []}
186
- else
187
- if resp.success?
188
- Oj.load(resp.body).fetch(:collection)
189
- else
190
- error_message = parse_error(resp)
191
- raise TeamSnap::Error.new(error_message)
192
- end
193
- end
42
+ ## Queries and Commands parsing for shortcut methods
43
+ TeamSnap::Collection.apply_endpoints(self, collection) && true
194
44
  end
195
45
 
196
- def run(via, href, args = {})
197
- resp = client_send(via, href, args)
198
- if resp.success?
199
- Oj.load(resp.body).fetch(:collection)
200
- else
201
- if resp.headers["content-type"].match("json")
202
- error_message = parse_error(resp)
203
- raise TeamSnap::Error.new(error_message)
204
- else
205
- raise TeamSnap::Error.new("`#{via}` call was unsuccessful. " +
206
- "Unexpected response content-type. " +
207
- "Check TeamSnap APIv3 connection")
208
- end
209
- end
46
+ def run(client, via, href, args = {})
47
+ resp = client_send(client, via, href, args)
48
+ return TeamSnap::Response.load_collection(resp)
49
+ rescue Faraday::TimeoutError
50
+ warn("Connection to API failed with TimeoutError")
51
+ {:links => []}
210
52
  end
211
53
 
212
- def client_send(via, href, args)
54
+ def client_send(client, via, href, args)
213
55
  case via
214
- when :get
56
+ when :get, :delete
215
57
  client.send(via, href, args)
216
- when :post
58
+ when :patch, :post
217
59
  client.send(via, href) do |req|
218
60
  req.body = Oj.dump(args)
219
61
  end
@@ -222,187 +64,5 @@ module TeamSnap
222
64
  end
223
65
  end
224
66
 
225
- def parse_error(resp)
226
- begin
227
- Oj.load(resp.body)
228
- .fetch(:collection)
229
- .fetch(:error)
230
- .fetch(:message)
231
- rescue KeyError
232
- resp.body
233
- end
234
- end
235
-
236
- def load_items(collection)
237
- collection
238
- .fetch(:items) { [] }
239
- .map { |item|
240
- data = parse_data(item).merge(:href => item[:href])
241
- type = type_of(item)
242
- cls = load_class(type, data)
243
-
244
- cls.new(data).tap { |obj|
245
- obj.send(:load_links, item.fetch(:links) { [] })
246
- }
247
- }
248
- end
249
-
250
- def apply_endpoints(obj, collection)
251
- collection
252
- .fetch(:queries) { [] }
253
- .each { |endpoint| register_endpoint(obj, endpoint, :via => :get) }
254
-
255
- collection
256
- .fetch(:commands) { [] }
257
- .each { |endpoint| register_endpoint(obj, endpoint, :via => :post) }
258
- end
259
-
260
- private
261
-
262
- attr_accessor :client
263
-
264
- def classify_rel(link)
265
- return if EXCLUDED_RELS.include?(link.fetch(:rel))
266
-
267
- rel = link.fetch(:rel)
268
- href = link.fetch(:href)
269
- resp = client.get(href)
270
-
271
- create_collection_class(rel, href, resp, nil)
272
- end
273
-
274
- def create_collection_class(rel, href, resp, collection)
275
- name = Inflecto.classify(rel)
276
- TeamSnap.const_set(
277
- name, Class.new { include TeamSnap.collection(href, resp, collection) }
278
- ) unless TeamSnap.const_defined?(name, false)
279
- end
280
-
281
- def register_endpoint(obj, endpoint, opts)
282
- rel = endpoint.fetch(:rel)
283
- href = endpoint.fetch(:href)
284
- valid_args = endpoint.fetch(:data) { [] }
285
- .map { |datum| datum.fetch(:name).to_sym }
286
- via = opts.fetch(:via)
287
-
288
- obj.define_singleton_method(rel) do |*args|
289
- args = Hash[*args]
290
-
291
- unless args.all? { |arg, _| valid_args.include?(arg) }
292
- raise ArgumentError.new(
293
- "Invalid argument(s). Valid argument(s) are #{valid_args.inspect}"
294
- )
295
- end
296
-
297
- TeamSnap.load_items(
298
- TeamSnap.run(via, href, args)
299
- )
300
- end
301
- end
302
-
303
- def parse_data(item)
304
- data = item
305
- .fetch(:data)
306
- .map { |datum|
307
- name = datum.fetch(:name)
308
- value = datum.fetch(:value)
309
- type = datum.fetch(:type) { :default }
310
-
311
- value = DateTime.parse(value) if value && type == "DateTime"
312
-
313
- [name, value]
314
- }
315
- TeamSnap.hashify(data)
316
- end
317
-
318
- def type_of(item)
319
- item
320
- .fetch(:data)
321
- .find { |datum| datum.fetch(:name) == "type" }
322
- .fetch(:value)
323
- end
324
-
325
- def load_class(type, data)
326
- TeamSnap.const_get(Inflecto.camelize(type), false).tap { |cls|
327
- unless cls.include?(Virtus::Model::Core)
328
- cls.class_eval do
329
- include Virtus.value_object
330
-
331
- values do
332
- attribute :href, String
333
- data.each { |name, value| attribute name, value.class }
334
- end
335
- end
336
- end
337
- }
338
- end
339
- end
340
-
341
- module Item
342
- private
343
-
344
- def load_links(links)
345
- links.each do |link|
346
- next if EXCLUDED_RELS.include?(link.fetch(:rel))
347
-
348
- rel = link.fetch(:rel)
349
- href = link.fetch(:href)
350
- is_singular = rel == Inflecto.singularize(rel)
351
-
352
- define_singleton_method(rel) {
353
- instance_variable_get("@#{rel}") || instance_variable_set(
354
- "@#{rel}", -> {
355
- coll = TeamSnap.load_items(
356
- TeamSnap.run(:get, href)
357
- )
358
- is_singular ? coll.first : coll
359
- }.call
360
- )
361
- }
362
- end
363
- end
364
- end
365
-
366
- module Collection
367
- def href
368
- self.instance_variable_get(:@href)
369
- end
370
-
371
- def resp
372
- self.instance_variable_get(:@resp)
373
- end
374
-
375
- def parsed_collection
376
- self.instance_variable_get(:@parsed_collection)
377
- end
378
-
379
- def parse_collection
380
- if resp
381
- if resp.status == 200
382
- collection = Oj.load(resp.body)
383
- .fetch(:collection) { [] }
384
- else
385
- error_message = TeamSnap.parse_error(resp)
386
- raise TeamSnap::Error.new(error_message)
387
- end
388
- elsif parsed_collection
389
- collection = parsed_collection
390
- end
391
-
392
- TeamSnap.apply_endpoints(self, collection)
393
- enable_find if respond_to?(:search)
394
- end
395
-
396
- private
397
-
398
- def enable_find
399
- define_singleton_method(:find) do |id|
400
- search(:id => id).first.tap do |object|
401
- raise TeamSnap::NotFound.new(
402
- "Could not find a #{self} with an id of '#{id}'."
403
- ) unless object
404
- end
405
- end
406
- end
407
67
  end
408
68
  end