clever-ruby 0.3.1 → 0.4.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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +14 -0
  3. data/.rubocop.yml +4 -0
  4. data/LICENSE +190 -0
  5. data/README.md +37 -7
  6. data/Rakefile +12 -2
  7. data/clever-ruby.gemspec +17 -13
  8. data/lib/clever-ruby.rb +87 -61
  9. data/lib/clever-ruby/api_operations/list.rb +9 -4
  10. data/lib/clever-ruby/api_operations/page.rb +7 -4
  11. data/lib/clever-ruby/api_operations/pagelist.rb +6 -3
  12. data/lib/clever-ruby/api_resource.rb +31 -10
  13. data/lib/clever-ruby/clever_object.rb +34 -35
  14. data/lib/clever-ruby/configuration.rb +2 -1
  15. data/lib/clever-ruby/district.rb +4 -12
  16. data/lib/clever-ruby/errors/api_connection_error.rb +1 -1
  17. data/lib/clever-ruby/errors/api_error.rb +1 -1
  18. data/lib/clever-ruby/errors/authentication_error.rb +1 -1
  19. data/lib/clever-ruby/errors/clever_error.rb +4 -3
  20. data/lib/clever-ruby/errors/invalid_request_error.rb +4 -3
  21. data/lib/clever-ruby/event.rb +6 -5
  22. data/lib/clever-ruby/json.rb +2 -1
  23. data/lib/clever-ruby/school.rb +2 -0
  24. data/lib/clever-ruby/section.rb +2 -0
  25. data/lib/clever-ruby/student.rb +6 -3
  26. data/lib/clever-ruby/teacher.rb +2 -0
  27. data/lib/clever-ruby/util.rb +31 -32
  28. data/lib/clever-ruby/version.rb +2 -1
  29. data/test/data/vcr_cassettes/districts.yml +10 -16
  30. data/test/data/vcr_cassettes/districts_event_pages.yml +627 -62
  31. data/test/data/vcr_cassettes/districts_events.yml +243 -41
  32. data/test/data/vcr_cassettes/districts_school_pages.yml +93 -102
  33. data/test/data/vcr_cassettes/districts_schools.yml +85 -92
  34. data/test/data/vcr_cassettes/districts_section_pages.yml +3364 -314
  35. data/test/data/vcr_cassettes/districts_sections.yml +591 -266
  36. data/test/data/vcr_cassettes/districts_student_pages.yml +1701 -14694
  37. data/test/data/vcr_cassettes/districts_students.yml +209 -2960
  38. data/test/data/vcr_cassettes/districts_students_filtered.yml +39 -64
  39. data/test/data/vcr_cassettes/districts_teacher_pages.yml +455 -202
  40. data/test/data/vcr_cassettes/districts_teachers.yml +244 -163
  41. data/test/data/vcr_cassettes/error_handling.yml +36 -52
  42. data/test/data/vcr_cassettes/schools.yml +20 -29
  43. data/test/data/vcr_cassettes/schools_optional_attributes.yml +21 -30
  44. data/test/data/vcr_cassettes/sections.yml +1069 -114
  45. data/test/data/vcr_cassettes/students.yml +1095 -1296
  46. data/test/data/vcr_cassettes/teachers.yml +2341 -872
  47. data/test/integration/api_operations/list_test.rb +16 -15
  48. data/test/integration/district_test.rb +18 -17
  49. data/test/integration/error_handling_test.rb +8 -7
  50. data/test/test_helper.rb +2 -2
  51. data/test/unit/clever_test.rb +13 -13
  52. data/test/unit/configuration_test.rb +8 -11
  53. data/test/unit/event_test.rb +18 -21
  54. data/test/unit/optional_attributes_test.rb +21 -14
  55. metadata +99 -55
  56. data/.travis.yml +0 -9
@@ -1,15 +1,20 @@
1
1
  module Clever
2
2
  module APIOperations
3
+ # A list of API resource instances
3
4
  module List
5
+ # Class methods for those that include List
4
6
  module ClassMethods
5
- def all(filters={})
6
- response = Clever.request(:get, url, filters)
7
- Util.convert_to_clever_object(response[:data])
7
+ def all(filters = {})
8
+ accum = []
9
+ Clever::APIOperations::PageList.new(url, filters).each do |page|
10
+ accum += page.all
11
+ end
12
+ accum
8
13
  end
9
14
  end
10
15
 
11
16
  def self.included(base)
12
- base.extend(ClassMethods)
17
+ base.extend ClassMethods
13
18
  end
14
19
  end
15
20
  end
@@ -1,19 +1,22 @@
1
1
  module Clever
2
2
  module APIOperations
3
+ # Represents a page of data
3
4
  class Page
4
5
  attr_accessor :paging
5
- def initialize(uri, filters={})
6
+
7
+ def initialize(uri, filters = {})
6
8
  @uri = uri
7
9
  @filters = filters
8
10
 
9
- response = Clever.request(:get, uri, filters)
10
- @list = Util.convert_to_clever_object(response[:data])
11
+ response = Clever.request :get, uri, filters
12
+ @list = Util.convert_to_clever_object response[:data]
11
13
  self.paging = response[:paging]
12
14
  end
13
15
 
16
+ # rubocop:disable TrivialAccessors
14
17
  def all
15
18
  @list
16
19
  end
17
20
  end
18
21
  end
19
- end
22
+ end
@@ -1,7 +1,10 @@
1
1
  module Clever
2
2
  module APIOperations
3
+ # Handles paginated requests.
4
+ # TODO: use rel links
5
+ # TODO: build functionality elsewhere
3
6
  class PageList
4
- def initialize(uri, filters={})
7
+ def initialize(uri, filters = {})
5
8
  @uri = uri
6
9
  @filters = filters
7
10
  end
@@ -10,7 +13,7 @@ module Clever
10
13
  current = 0
11
14
  total = 1
12
15
  while current < total
13
- page = Page.new(@uri, @filters.merge({ page: current + 1 }))
16
+ page = Page.new @uri, @filters.merge(page: current + 1)
14
17
 
15
18
  yield page
16
19
 
@@ -20,4 +23,4 @@ module Clever
20
23
  end
21
24
  end
22
25
  end
23
- end
26
+ end
@@ -1,35 +1,56 @@
1
1
  module Clever
2
+ # Superclass of API resources in the Clever API
2
3
  class APIResource < CleverObject
3
4
  def self.url
4
5
  if self == APIResource
5
- raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (School, Student, etc.)')
6
+ fail NotImplementedError, 'APIResource is an abstract class. You should perform actions '\
7
+ 'on its subclasses (School, Student, etc.)'
6
8
  end
7
- shortname = self.name.split('::')[-1]
8
- "v1.1/#{CGI.escape(shortname.downcase)}s"
9
+ shortname = name.split('::')[-1]
10
+ "v1.1/#{CGI.escape shortname.downcase}s"
9
11
  end
10
12
 
11
13
  def url
12
- unless id = self['id']
13
- raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
14
+ id = self['id']
15
+ unless id
16
+ fail InvalidRequestError.new(
17
+ "Could not determine which URL to request: #{self.class} instance has " \
18
+ "invalid ID: #{id.inspect}", 'id')
14
19
  end
15
- "#{self.class.url}/#{CGI.escape(id)}"
20
+ "#{self.class.url}/#{CGI.escape id}"
16
21
  end
17
22
 
18
23
  def refresh
19
- response = Clever.request(:get, url)
20
- refresh_from(response[:data])
24
+ response = Clever.request :get, url
25
+ refresh_from response[:data]
21
26
  self
22
27
  end
23
28
 
24
29
  def links
25
- response = Clever.request(:get, url)
30
+ response = Clever.request :get, url
26
31
  response[:links]
27
32
  end
28
33
 
29
34
  def self.retrieve(id)
30
- instance = self.new(id)
35
+ instance = new id
31
36
  instance.refresh
32
37
  instance
33
38
  end
39
+
40
+ def get_linked_resources(resource_type, filters = {})
41
+ Util.convert_to_clever_object Clever.request(:get, get_uri(resource_type), filters)[:data]
42
+ end
43
+
44
+ class << self; attr_reader :linked_resources; end
45
+ def initialize(id)
46
+ super id
47
+
48
+ resources = self.class.linked_resources || []
49
+ resources.each do |resource|
50
+ self.class.send :define_method, resource do |filters = {}|
51
+ get_linked_resources resource.to_s, filters
52
+ end
53
+ end
54
+ end
34
55
  end
35
56
  end
@@ -1,45 +1,46 @@
1
1
  module Clever
2
+ # An instance of an APIResource's contents
2
3
  class CleverObject
3
4
  include Enumerable
4
5
 
5
- @@permanent_attributes = Set.new([])
6
+ # TODO: fix this
7
+ # rubocop:disable ClassVars
8
+ @@permanent_attributes = Set.new []
6
9
 
7
10
  # The default :id method is deprecated and isn't useful to us
8
- if method_defined?(:id)
9
- undef :id
10
- end
11
+ undef :id if method_defined? :id
11
12
 
12
- def initialize(id=nil)
13
+ def initialize(id = nil)
13
14
  @values = {}
14
15
  @values[:id] = id if id
15
16
  end
16
17
 
17
18
  def self.construct_from(values)
18
- obj = self.new(values[:id])
19
- obj.refresh_from(values)
19
+ obj = new values[:id]
20
+ obj.refresh_from values
20
21
  obj
21
22
  end
22
23
 
23
- def to_s(*args)
24
- Clever::JSON.dump(@values, :pretty => true)
24
+ def to_s
25
+ Clever::JSON.dump @values, pretty: true
25
26
  end
26
27
 
27
- def inspect()
28
- id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
29
- "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + Clever::JSON.dump(@values, :pretty => true)
28
+ def inspect
29
+ id_string = (respond_to?(:id) && !id.nil?) ? " id=#{id}" : ''
30
+ "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " +
31
+ Clever::JSON.dump(@values, pretty: true)
30
32
  end
31
33
 
32
- def refresh_from(values, partial=false)
33
-
34
+ def refresh_from(values, partial = false)
34
35
  removed = partial ? Set.new : Set.new(@values.keys - values.keys)
35
36
  added = Set.new(values.keys - @values.keys)
36
37
 
37
38
  instance_eval do
38
- remove_accessors(removed)
39
- add_accessors(added)
39
+ remove_accessors removed
40
+ add_accessors added
40
41
  end
41
42
  removed.each do |k|
42
- @values.delete(k)
43
+ @values.delete k
43
44
  end
44
45
  values.each do |k, v|
45
46
  # Stripe apparently allows you to have nested object types (e.g.
@@ -50,12 +51,12 @@ module Clever
50
51
  end
51
52
 
52
53
  def [](k)
53
- k = k.to_sym if k.kind_of?(String)
54
+ k = k.to_sym if k.is_a? String
54
55
  @values[k]
55
56
  end
56
57
 
57
58
  def []=(k, v)
58
- send(:"#{k}=", v)
59
+ send :"#{k}=", v
59
60
  end
60
61
 
61
62
  def keys
@@ -66,8 +67,8 @@ module Clever
66
67
  @values.values
67
68
  end
68
69
 
69
- def to_json(*a)
70
- Clever::JSON.dump(@values)
70
+ def to_json
71
+ Clever::JSON.dump @values
71
72
  end
72
73
 
73
74
  def as_json(*a)
@@ -82,10 +83,8 @@ module Clever
82
83
  @values.each(&blk)
83
84
  end
84
85
 
85
- def ==( other )
86
- if other.respond_to?( :values )
87
- self.values == other.values
88
- end
86
+ def ==(other)
87
+ values == other.values if other.respond_to? :values
89
88
  end
90
89
 
91
90
  protected
@@ -97,10 +96,10 @@ module Clever
97
96
  def remove_accessors(keys)
98
97
  metaclass.instance_eval do
99
98
  keys.each do |k|
100
- next if @@permanent_attributes.include?(k)
99
+ next if @@permanent_attributes.include? k
101
100
  k_eq = :"#{k}="
102
- remove_method(k) if method_defined?(k)
103
- remove_method(k_eq) if method_defined?(k_eq)
101
+ remove_method k if method_defined? k
102
+ remove_method k_eq if method_defined? k_eq
104
103
  end
105
104
  end
106
105
  end
@@ -108,23 +107,23 @@ module Clever
108
107
  def add_accessors(keys)
109
108
  metaclass.instance_eval do
110
109
  keys.each do |k|
111
- next if @@permanent_attributes.include?(k)
110
+ next if @@permanent_attributes.include? k
112
111
  k_eq = :"#{k}="
113
112
  define_method(k) { @values[k] }
114
- define_method(k_eq) do |v|
115
- @values[k] = v
116
- end
113
+ define_method(k_eq) { |v| @values[k] = v }
117
114
  end
118
115
  end
119
116
  end
120
117
 
121
118
  def optional_attributes
122
- raise NotImplementedError.new('Please define #optional_attributes as a list of the attributes on this resource that may not be present and thus should return nil instead of raising a NoMethodError.')
119
+ fail NotImplementedError 'Please define #optional_attributes as a list of the '\
120
+ 'attributes on this resource that may not be present and thus should return nil' \
121
+ 'instead of raising a NoMethodError.'
123
122
  end
124
123
 
125
124
  def method_missing(name, *args)
126
- return @values[name] if @values.has_key?(name)
127
- return nil if optional_attributes.include?(name)
125
+ return @values[name] if @values.key? name
126
+ return nil if optional_attributes.include? name
128
127
  super
129
128
  end
130
129
  end
@@ -1,4 +1,5 @@
1
1
  module Clever
2
+ # Configuration for accessing the Clever API over HTTP
2
3
  class Configuration
3
4
  attr_accessor :api_key, :token, :api_base
4
5
 
@@ -8,4 +9,4 @@ module Clever
8
9
  @api_base = 'https://api.clever.com/'
9
10
  end
10
11
  end
11
- end
12
+ end
@@ -1,33 +1,25 @@
1
1
  module Clever
2
+ # District resource
2
3
  class District < APIResource
3
4
  include Clever::APIOperations::List
5
+ @linked_resources = [:schools, :teachers, :sections, :students, :events]
4
6
 
5
7
  def optional_attributes
6
8
  # All of a district's attributes are required.
7
9
  []
8
10
  end
9
11
 
10
- [:schools, :teachers, :sections, :students, :events].each do |name|
11
- define_method(name) do |filters = {}|
12
- get_linked_resources name.to_s, filters
13
- end
14
- end
15
-
16
12
  [:school_pages, :teacher_pages, :section_pages, :student_pages, :event_pages].each do |name|
17
13
  define_method(name) do |filters = {}|
18
- Clever::APIOperations::PageList.new(get_uri(name.to_s.gsub('_page', '')), filters)
14
+ Clever::APIOperations::PageList.new get_uri(name.to_s.gsub('_page', '')), filters
19
15
  end
20
16
  end
21
17
 
22
18
  private
23
19
 
24
- def get_linked_resources(resource_type, filters={})
25
- Util.convert_to_clever_object(Clever.request(:get, get_uri(resource_type), filters)[:data])
26
- end
27
-
28
20
  def get_uri(resource_type)
29
21
  refresh
30
- links.detect {|link| link[:rel] == resource_type }[:uri]
22
+ links.find { |link| link[:rel] == resource_type }[:uri]
31
23
  end
32
24
  end
33
25
  end
@@ -1,4 +1,4 @@
1
1
  module Clever
2
2
  class APIConnectionError < CleverError
3
3
  end
4
- end
4
+ end
@@ -1,4 +1,4 @@
1
1
  module Clever
2
2
  class APIError < CleverError
3
3
  end
4
- end
4
+ end
@@ -1,4 +1,4 @@
1
1
  module Clever
2
2
  class AuthenticationError < CleverError
3
3
  end
4
- end
4
+ end
@@ -1,11 +1,12 @@
1
1
  module Clever
2
+ # Represents an error outputted bythe Clever API
2
3
  class CleverError < StandardError
3
4
  attr_reader :message
4
5
  attr_reader :http_status
5
6
  attr_reader :http_body
6
7
  attr_reader :json_body
7
8
 
8
- def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ def initialize(message = nil, http_status = nil, http_body = nil, json_body = nil)
9
10
  @message = message
10
11
  @http_status = http_status
11
12
  @http_body = http_body
@@ -13,8 +14,8 @@ module Clever
13
14
  end
14
15
 
15
16
  def to_s
16
- status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
+ status_string = @http_status.nil? ? '' : "(Status #{@http_status}) "
17
18
  "#{status_string}#{@message}"
18
19
  end
19
20
  end
20
- end
21
+ end
@@ -1,10 +1,11 @@
1
1
  module Clever
2
+ # An invalid request to the Clever API
2
3
  class InvalidRequestError < CleverError
3
4
  attr_accessor :param
4
5
 
5
- def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
6
- super(message, http_status, http_body, json_body)
6
+ def initialize(message, param, http_status = nil, http_body = nil, json_body = nil)
7
+ super message, http_status, http_body, json_body
7
8
  @param = param
8
9
  end
9
10
  end
10
- end
11
+ end
@@ -1,4 +1,5 @@
1
1
  module Clever
2
+ # Event resource
2
3
  class Event < APIResource
3
4
  include Clever::APIOperations::List
4
5
 
@@ -7,9 +8,9 @@ module Clever
7
8
  end
8
9
 
9
10
  def object
10
- klass = Util.types_to_clever_class(type_pieces[0])
11
+ klass = Util.types_to_clever_class type_pieces[0]
11
12
  klass ||= CleverObject
12
- klass.construct_from(data[:object])
13
+ klass.construct_from data[:object]
13
14
  end
14
15
 
15
16
  def previous_attributes
@@ -19,15 +20,15 @@ module Clever
19
20
  def action
20
21
  type_pieces[1]
21
22
  end
22
-
23
+
23
24
  def self.url
24
- "v1.1/events"
25
+ 'v1.1/events'
25
26
  end
26
27
 
27
28
  private
28
29
 
29
30
  def type_pieces
30
- type.split(".")
31
+ type.split '.'
31
32
  end
32
33
  end
33
34
  end
@@ -1,6 +1,7 @@
1
1
  module Clever
2
+ # Helpers for handling JSON responses
2
3
  module JSON
3
- if MultiJson.respond_to?(:dump)
4
+ if MultiJson.respond_to? :dump
4
5
  def self.dump(*args)
5
6
  MultiJson.dump(*args)
6
7
  end