clever-ruby 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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