bright 0.1.0 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 381169bba4d0de609bb9650455e7d8e4568d9de4
4
- data.tar.gz: 53b5f910b2a5655a194b6a913f75c5a67b1753c5
2
+ SHA256:
3
+ metadata.gz: dd99510122d136894a8e1ba1fdfb6bea0c424f355624ca2c68178143fcd7965d
4
+ data.tar.gz: 16b8599ad55c91cf1f925a76da5d49690c49e2315ca63b925b71d31e421de45c
5
5
  SHA512:
6
- metadata.gz: 1d8fe64474322364c96ebb6f51d30a944b7cb0ba1e3f207f5dba7cb99cb415cbb01a197155c5af9d4f8444354277debc97713c5ea2011a10eba671ce8068ec45
7
- data.tar.gz: 8f0a9ea2b0f0299deb7bb3be69713d42b3d315f120af8a045882befa92f992436eae8b5b7690a93db8258de13bfb073ca0b2c3369cc0b3cfaea121ea0c15cfe4
6
+ metadata.gz: e4499da391786116fef2ef77fbf283fd67f4c4f26f84277f2bf75825d9b2444712f7df8b7bb99ba3279f4c10612addbaaa47dda84ffda2f8e9b4240539d80939
7
+ data.tar.gz: b544283e756c7114aac543d7b7a3c4d6acf87de7b5c58593a96bcafa619628444a7eaa91b0256dada1e28519914d381fc022563dce10814b5e8d0dbccca74736
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Stephen Heuer
1
+ Copyright (c) 2022 Arux Software, Inc.
2
2
 
3
3
  MIT License
4
4
 
data/bright.gemspec CHANGED
@@ -7,9 +7,9 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "bright"
8
8
  spec.version = Bright::VERSION
9
9
  spec.authors = ["Arux Software"]
10
- spec.email = ["sheuer@aruxsoftware.com"]
11
- spec.summary = "Framework and tools for dealing with Student Information Systems"
12
- spec.description = "Bright is a simple Student Information System API abstraction library used in and sponsored by FeePay. It is written by Stephen Heuer, Steven Novotny, and contributors. The aim of the project is to abstract as many parts as possible away from the user to offer a consistent interface across all supported Student Information System APIs."
10
+ spec.email = ["hello@arux.software"]
11
+ spec.summary = "Framework and tools for dealing with Student Information Systems"
12
+ spec.description = "Bright is a simple Student Information System API abstraction library used in and sponsored by Arux Software. It is written by Stephen Heuer, Steven Novotny, and contributors. The aim of the project is to abstract as many parts as possible away from the user to offer a consistent interface across all supported Student Information System APIs."
13
13
  spec.homepage = ""
14
14
  spec.license = "MIT"
15
15
 
@@ -20,7 +20,9 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_runtime_dependency "httpi", "~> 2.1"
22
22
  spec.add_runtime_dependency "json", ">= 0"
23
+ spec.add_runtime_dependency "oauth", ">= 0.5.4"
24
+ spec.add_runtime_dependency "parallel", "~> 1.2"
23
25
 
24
- spec.add_development_dependency "bundler", "~> 1.7"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "bundler", ">= 2.2.10"
27
+ spec.add_development_dependency "rake", "~> 12.3.3"
26
28
  end
@@ -1,17 +1,15 @@
1
1
  module Bright
2
2
  class Address < Model
3
- @attribute_names = [:street, :apt, :city, :state, :postal_code, :lattitude, :longitude, :type]
3
+ @attribute_names = [:street, :apt, :city, :state, :postal_code, :latitude, :longitude, :type]
4
4
  attr_accessor *@attribute_names
5
-
6
- alias lat lattitude
5
+
6
+ alias lat latitude
7
7
  alias lng longitude
8
-
8
+
9
9
  def geographical_coordinates
10
- if self.lattitude and self.longitude
11
- "#{self.lattitude},#{self.longitude}"
10
+ if self.latitude and self.longitude
11
+ "#{self.latitude},#{self.longitude}"
12
12
  end
13
13
  end
14
14
  end
15
15
  end
16
-
17
-
@@ -1,6 +1,7 @@
1
1
  require 'uri'
2
2
  require 'httpi'
3
3
  require 'benchmark'
4
+ require 'securerandom'
4
5
 
5
6
  module Bright
6
7
  class Connection
@@ -30,72 +31,79 @@ module Bright
30
31
  @ssl_version = nil
31
32
  @proxy_address = nil
32
33
  @proxy_port = nil
34
+
35
+ if Bright.devmode && !@logger
36
+ @logger = Logger.new(STDOUT)
37
+ @logger.level = Logger::INFO
38
+ end
39
+
33
40
  end
34
41
 
35
42
  def request(method, body, headers = {})
36
43
  request_start = Time.now.to_f
44
+ info "connection_http_method=#{method.to_s.upcase} connection_uri=#{endpoint} headers=#{headers.inspect}", tag
45
+
46
+ result = nil
37
47
 
38
- begin
39
- info "connection_http_method=#{method.to_s.upcase} connection_uri=#{endpoint}", tag
40
-
41
- result = nil
42
-
43
- realtime = Benchmark.realtime do
44
- request = HTTPI::Request.new(endpoint.to_s)
45
- request.headers = headers
46
- request.body = body if body
47
- request.auth.ssl.verify_mode = :none if !@verify_peer
48
- configure_proxy(request)
49
- configure_timeouts(request)
50
-
51
- result = case method
52
- when :get
53
- raise ArgumentError, "GET requests do not support a request body" if body
54
- HTTPI.get(request)
55
- when :post
56
- debug body
57
- HTTPI.post(request)
58
- when :put
59
- debug body
60
- HTTPI.put(request)
61
- when :patch
62
- debug body
63
- HTTPI.patch(request)
64
- when :delete
65
- HTTPI.delete(request)
66
- else
67
- raise ArgumentError, "Unsupported request method #{method.to_s.upcase}"
68
- end
48
+ if !Bright.devmode
49
+ HTTPI.log = false
50
+ end
51
+
52
+ realtime = Benchmark.realtime do
53
+ request = HTTPI::Request.new(endpoint.to_s)
54
+ request.headers = headers
55
+ request.body = body if body
56
+ request.auth.ssl.verify_mode = :none if !@verify_peer
57
+ configure_proxy(request)
58
+ configure_timeouts(request)
59
+
60
+ result = case method
61
+ when :get
62
+ raise ArgumentError, "GET requests do not support a request body" if body
63
+ HTTPI.get(request)
64
+ when :post
65
+ debug(body) if Bright.devmode
66
+ HTTPI.post(request)
67
+ when :put
68
+ debug(body) if Bright.devmode
69
+ HTTPI.put(request)
70
+ when :patch
71
+ debug(body) if Bright.devmode
72
+ HTTPI.patch(request)
73
+ when :delete
74
+ HTTPI.delete(request)
75
+ else
76
+ raise ArgumentError, "Unsupported request method #{method.to_s.upcase}"
69
77
  end
78
+ end
70
79
 
71
- info "--> %d (%d %.4fs)" % [result.code, result.body ? result.body.length : 0, realtime], tag
72
- debug result.body
73
- result
80
+ if Bright.devmode
81
+ info("--> %d (%d %.4fs)" % [result.code, result.body ? result.body.length : 0, realtime], tag)
82
+ debug(result.body)
74
83
  end
84
+
85
+ handle_response(result)
86
+
75
87
  ensure
76
88
  info "connection_request_total_time=%.4fs" % [Time.now.to_f - request_start], tag
77
89
  end
78
90
 
79
91
  private
92
+
80
93
  def configure_proxy(http)
81
94
  http.proxy = "#{proxy_address}:#{proxy_port}" if proxy_address
82
95
  end
83
-
96
+
84
97
  def configure_timeouts(http)
85
98
  http.open_timeout = open_timeout
86
99
  http.read_timeout = read_timeout
87
100
  end
88
101
 
89
102
  def handle_response(response)
90
- if @ignore_http_status then
91
- return response.body
103
+ if @ignore_http_status or !response.error?
104
+ return response
92
105
  else
93
- case response.code.to_i
94
- when 200...300
95
- response.body
96
- else
97
- raise ResponseError.new(response)
98
- end
106
+ raise ResponseError.new(response, endpoint.to_s)
99
107
  end
100
108
  end
101
109
 
@@ -116,4 +124,4 @@ module Bright
116
124
  logger.send(level, message) if logger
117
125
  end
118
126
  end
119
- end
127
+ end
@@ -0,0 +1,53 @@
1
+ require 'securerandom'
2
+
3
+ module Bright
4
+ class Contact < Model
5
+ @attribute_names = [:client_id, :api_id, :first_name, :middle_name, :last_name, :nick_name,
6
+ :birth_date, :gender, :relationship_type,
7
+ :hispanic_ethnicity, :race, :image,
8
+ :sis_student_id, :state_student_id, :last_modified]
9
+ attr_accessor *@attribute_names
10
+
11
+ def self.attribute_names
12
+ @attribute_names
13
+ end
14
+
15
+ attr_accessor :phone_numbers, :addresses, :email_address
16
+
17
+ def phone_numbers=(array)
18
+ if array.size <= 0 or array.first.is_a?(PhoneNumber)
19
+ @phone_numbers = array
20
+ elsif array.first.is_a?(Hash)
21
+ @phone_numbers = array.collect{|a| PhoneNumber.new(a)}
22
+ end
23
+ @phone_numbers ||= []
24
+ end
25
+
26
+ def phone_numbers
27
+ @phone_numbers ||= []
28
+ end
29
+
30
+ def addresses=(array)
31
+ if array.size <= 0 or array.first.is_a?(Address)
32
+ @addresses = array
33
+ elsif array.first.is_a?(Hash)
34
+ @addresses = array.collect{|a| Address.new(a)}
35
+ end
36
+ @addresses ||= []
37
+ end
38
+
39
+ def addresses
40
+ @addresses ||= []
41
+ end
42
+
43
+ def email_address=(email)
44
+ if email.is_a?(EmailAddress)
45
+ @email_address = email
46
+ elsif email.is_a?(Hash)
47
+ @email_address = EmailAddress.new(email)
48
+ end
49
+ @email_address
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ class CursorResponseCollection < ResponseCollection
2
+
3
+ attr_accessor :collected_objects
4
+
5
+ def initialize(options = {})
6
+ @collected_objects = [options[:seed_page]].flatten
7
+ @per_page = options[:per_page].to_i
8
+ @load_more_call = options[:load_more_call]
9
+ @next_cursor = options[:next_cursor]
10
+ end
11
+
12
+ def each
13
+ while (!@next_cursor.blank?) do
14
+ objects_hsh = load_more_call.call(@next_cursor)
15
+ objects = objects_hsh[:objects]
16
+ @next_cursor = objects_hsh[:next_cursor]
17
+ objects.each do |obj|
18
+ yield obj
19
+ end
20
+ @collected_objects += objects
21
+ end
22
+ end
23
+
24
+ def last
25
+ self.to_a
26
+ return @collected_objects.last
27
+ end
28
+
29
+ def loaded_results
30
+ @collected_objects.flatten
31
+ end
32
+
33
+ def total
34
+ self.to_a
35
+ self.loaded_results.size
36
+ end
37
+
38
+ alias size total
39
+ alias length total
40
+
41
+ def empty?
42
+ self.to_a.empty?
43
+ end
44
+
45
+ end
@@ -0,0 +1,7 @@
1
+ module Bright
2
+ class EmailAddress < Model
3
+ @attribute_names = [:email_address]
4
+ attr_accessor *@attribute_names
5
+
6
+ end
7
+ end
data/lib/bright/errors.rb CHANGED
@@ -1,15 +1,25 @@
1
1
  module Bright
2
2
  class ResponseError < StandardError
3
3
  attr_reader :response
4
+ attr_reader :uri
4
5
 
5
- def initialize(response, message = nil)
6
+ def initialize(response, uri = nil)
6
7
  @response = response
7
- @message = message
8
+ @uri = uri
8
9
  end
9
10
 
10
11
  def to_s
11
- "Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
12
+ "Failed with #{response.code} #{response.message if response.respond_to?(:message)}".strip
12
13
  end
14
+
15
+ def body
16
+ response.body
17
+ end
18
+
19
+ def server_error?
20
+ (500..599).include?(response&.code.to_i)
21
+ end
22
+
13
23
  end
14
24
 
15
25
  class UnknownAttributeError < NoMethodError
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ class Object
4
+ def blank?
5
+ respond_to?(:empty?) ? !!empty? : !self
6
+ end
7
+
8
+ def present?
9
+ !blank?
10
+ end
11
+
12
+ def presence
13
+ self if present?
14
+ end
15
+ end
16
+
17
+ class NilClass
18
+ def blank?
19
+ true
20
+ end
21
+ end
22
+
23
+ class FalseClass
24
+ def blank?
25
+ true
26
+ end
27
+ end
28
+
29
+ class TrueClass
30
+ def blank?
31
+ false
32
+ end
33
+ end
34
+
35
+ class Array
36
+ alias_method :blank?, :empty?
37
+ end
38
+
39
+ class Hash
40
+ alias_method :blank?, :empty?
41
+ end
42
+
43
+ class String
44
+ BLANK_RE = /\A[[:space:]]*\z/
45
+
46
+ def blank?
47
+ BLANK_RE === self
48
+ end
49
+ end
50
+
51
+ class Numeric
52
+ def blank?
53
+ false
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ module Kernel
2
+ def Boolean(string)
3
+ return true if string == true || string =~ /^true$/i || string == "1" || string == 1 || string.to_s.downcase == "yes"
4
+ return false if string == false || string.nil? || string =~ /^false$/i || string == "0" || string == 0 || string.to_s.downcase == "no" || string.blank?
5
+ raise ArgumentError.new("invalid value for Boolean: \"#{string}\"")
6
+ end
7
+ end
8
+
9
+ class Object
10
+ def to_bool
11
+ Boolean(self)
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module Bright
2
+ class PhoneNumber < Model
3
+ @attribute_names = [:phone_number, :extension, :type]
4
+ attr_accessor *@attribute_names
5
+ TYPES = ["Cell", "Home", "Work", "Other"]
6
+
7
+ def phone_number=(number)
8
+ number_a = number.to_s.split(/x|X/)
9
+ if number_a.size == 2
10
+ @extension = number_a.last.gsub(/[^0-9]/, "").strip
11
+ end
12
+ @phone_number = number_a.first.gsub(/[^0-9]/, "").strip
13
+ end
14
+
15
+ def extension=(number)
16
+ @extension = number.gsub(/[^0-9]/, "").strip
17
+ end
18
+
19
+ end
20
+ end
@@ -1,11 +1,15 @@
1
+ require 'parallel'
2
+
1
3
  class ResponseCollection
2
4
  include Enumerable
3
-
5
+
4
6
  attr_accessor :paged_objects
5
7
  attr_accessor :total
6
8
  attr_accessor :per_page
7
9
  attr_accessor :load_more_call
8
-
10
+
11
+ DEFAULT_NO_THREADS = 4
12
+
9
13
  # seed_page, total, per_page, load_more_call
10
14
  def initialize(options = {})
11
15
  @paged_objects = {0 => options[:seed_page]}
@@ -13,27 +17,24 @@ class ResponseCollection
13
17
  @per_page = options[:per_page].to_i
14
18
  @pages = @per_page > 0 ? (@total.to_f / @per_page.to_f).ceil : 0
15
19
  @load_more_call = options[:load_more_call]
20
+ @no_threads = options[:no_threads] || DEFAULT_NO_THREADS
16
21
  end
17
-
22
+
18
23
  def each
19
- current_page = -1
20
- while (current_page += 1) < @pages do
21
- objects = [@paged_objects[current_page]].flatten.compact
22
- next_page_no = current_page + 1
23
- if load_more_call and @paged_objects[next_page_no].nil? and next_page_no < @pages
24
- next_page_thread = Thread.new do
25
- load_more_call.call(next_page_no)
26
- end
27
- else
28
- next_page_thread = nil
29
- end
24
+ Parallel.each(0..@pages, in_threads: @no_threads) do |current_page|
25
+ objects = if @paged_objects[current_page].present?
26
+ @paged_objects[current_page]
27
+ else
28
+ load_more_call.call(current_page)
29
+ end
30
+ objects = [objects].flatten.compact
31
+ @paged_objects[current_page] = objects if objects.present?
30
32
  objects.each do |obj|
31
33
  yield obj
32
34
  end
33
- @paged_objects[next_page_no] = next_page_thread.value if next_page_thread
34
35
  end
35
36
  end
36
-
37
+
37
38
  def last
38
39
  last_page_no = @pages - 1
39
40
  if load_more_call and (last_page = @paged_objects[last_page_no]).nil?
@@ -41,15 +42,15 @@ class ResponseCollection
41
42
  end
42
43
  last_page.last
43
44
  end
44
-
45
+
45
46
  def loaded_results
46
47
  @paged_objects.values.flatten
47
48
  end
48
-
49
+
49
50
  alias size total
50
51
  alias length total
51
52
 
52
53
  def empty?
53
54
  total <= 0
54
55
  end
55
- end
56
+ end
data/lib/bright/school.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  module Bright
2
2
  class School < Model
3
- @attribute_names = [:api_id, :name, :number]
3
+ @attribute_names = [:api_id, :name, :number, :state_id, :low_grade, :high_grade, :last_modified]
4
4
  attr_accessor *@attribute_names
5
- attr_accessor :address
6
-
5
+ attr_accessor :address, :phone_number
6
+
7
7
  def address=(address)
8
8
  if address.is_a?(Address)
9
9
  @address = address
@@ -12,6 +12,15 @@ module Bright
12
12
  end
13
13
  @address
14
14
  end
15
-
15
+
16
+ def phone_number=(phone_number)
17
+ if phone_number.is_a?(PhoneNumber)
18
+ @phone_number = phone_number
19
+ elsif phone_number.is_a?(Hash)
20
+ @phone_number = PhoneNumber.new(phone_number)
21
+ end
22
+ @phone_number
23
+ end
24
+
16
25
  end
17
26
  end