teamsnap_rb 1.3.3 → 2.0.0.beta

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.
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