school_loop 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07ac5da7b48f65a5d323294763459397ec01ebe6
4
+ data.tar.gz: c019c80fbdc560ac6810f163d251e8a6e6d76dc0
5
+ SHA512:
6
+ metadata.gz: b41f0caca0c1850d4ccc4415265d91fd6084e1cf51873fb8a5205991fdb31ceced125b83a75780333fcab1534314715a5accf87f96d3de781edf8afa7b55ab56
7
+ data.tar.gz: b6d0ed1e89c2aa21318afc7dc45c9a55f2bf9f9201ae296ed01c5f6edcad02f5f8d08649e2e83954307a0457925c3cf228a961a7b911d7851970c3c46d978153
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ coverage/
2
+ doc
3
+ .yardoc/
4
+ pkg/
5
+ .env
6
+ roster.xml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.2.0
6
+ script: bundle exec rake
7
+ before_install:
8
+ - gem update --system
9
+ - gem install bundler
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://www.rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ group :development do
6
+ gem 'pry'
7
+ end
8
+
9
+ group :test do
10
+ gem 'coveralls'
11
+ gem 'rspec', '~> 3.3'
12
+ gem 'simplecov', :require => false
13
+ gem 'webmock', '>= 1.9'
14
+ end
15
+
16
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ school_loop (0.0.1)
5
+ faraday (~> 0.9)
6
+ faraday_middleware (~> 0.9)
7
+ nokogiri (>= 1.5.2)
8
+
9
+ GEM
10
+ remote: https://www.rubygems.org/
11
+ specs:
12
+ addressable (2.3.8)
13
+ coderay (1.1.0)
14
+ coveralls (0.8.2)
15
+ json (~> 1.8)
16
+ rest-client (>= 1.6.8, < 2)
17
+ simplecov (~> 0.10.0)
18
+ term-ansicolor (~> 1.3)
19
+ thor (~> 0.19.1)
20
+ crack (0.4.2)
21
+ safe_yaml (~> 1.0.0)
22
+ diff-lcs (1.2.5)
23
+ docile (1.1.5)
24
+ domain_name (0.5.24)
25
+ unf (>= 0.0.5, < 1.0.0)
26
+ faraday (0.9.1)
27
+ multipart-post (>= 1.2, < 3)
28
+ faraday_middleware (0.10.0)
29
+ faraday (>= 0.7.4, < 0.10)
30
+ http-cookie (1.0.2)
31
+ domain_name (~> 0.5)
32
+ json (1.8.3)
33
+ method_source (0.8.2)
34
+ mime-types (1.25.1)
35
+ mini_portile (0.6.2)
36
+ multipart-post (2.0.0)
37
+ netrc (0.10.3)
38
+ nokogiri (1.6.6.2)
39
+ mini_portile (~> 0.6.0)
40
+ pry (0.10.1)
41
+ coderay (~> 1.1.0)
42
+ method_source (~> 0.8.1)
43
+ slop (~> 3.4)
44
+ rake (10.4.2)
45
+ rest-client (1.8.0)
46
+ http-cookie (>= 1.0.2, < 2.0)
47
+ mime-types (>= 1.16, < 3.0)
48
+ netrc (~> 0.7)
49
+ rspec (3.3.0)
50
+ rspec-core (~> 3.3.0)
51
+ rspec-expectations (~> 3.3.0)
52
+ rspec-mocks (~> 3.3.0)
53
+ rspec-core (3.3.2)
54
+ rspec-support (~> 3.3.0)
55
+ rspec-expectations (3.3.1)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.3.0)
58
+ rspec-mocks (3.3.2)
59
+ diff-lcs (>= 1.2.0, < 2.0)
60
+ rspec-support (~> 3.3.0)
61
+ rspec-support (3.3.0)
62
+ safe_yaml (1.0.4)
63
+ simplecov (0.10.0)
64
+ docile (~> 1.1.0)
65
+ json (~> 1.8)
66
+ simplecov-html (~> 0.10.0)
67
+ simplecov-html (0.10.0)
68
+ slop (3.6.0)
69
+ term-ansicolor (1.3.2)
70
+ tins (~> 1.0)
71
+ thor (0.19.1)
72
+ tins (1.5.4)
73
+ unf (0.1.4)
74
+ unf_ext
75
+ unf_ext (0.0.7.1)
76
+ webmock (1.21.0)
77
+ addressable (>= 2.3.6)
78
+ crack (>= 0.3.2)
79
+
80
+ PLATFORMS
81
+ ruby
82
+
83
+ DEPENDENCIES
84
+ bundler (~> 1.0)
85
+ coveralls
86
+ pry
87
+ rake
88
+ rspec (~> 3.3)
89
+ school_loop!
90
+ simplecov
91
+ webmock (>= 1.9)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 Mitch Dempsey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # School Loop API
2
+
3
+
4
+ [![Build Status](https://travis-ci.org/webdestroya/school_loop.svg)](https://travis-ci.org/webdestroya/school_loop)
5
+
6
+
7
+ ## Installation
8
+
9
+ Install the gem by issuing
10
+
11
+ ```ruby
12
+ gem install school_loop
13
+ ```
14
+
15
+ or put it in your Gemfile and run `bundle install`
16
+
17
+ ```ruby
18
+ gem "school_loop"
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ These global settings will be used unless specifically overridden by an instance below.
24
+
25
+ ```ruby
26
+ SchoolLoop.configure do |config|
27
+ config.subdomain = 'demoschool'
28
+ config.username = 'apiuser'
29
+ config.password = 'some_api_password'
30
+ config.adapter = :net_http
31
+ end
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ Create a new client using the global defaults:
37
+
38
+ ```ruby
39
+ school_loop = SchoolLoop.new
40
+ ```
41
+
42
+ Create a new client instance and override global defaults
43
+
44
+ ```ruby
45
+ school_loop = SchoolLoop.new subdomain: 'demoschool', username: 'apiuser', password: 'some_api_password'
46
+ ```
47
+
48
+ Alternatively, you can configure the School Loop settings by passing a block:
49
+
50
+ ```ruby
51
+ school_loop = SchoolLoop.new do |config|
52
+ config.subdomain = 'demoschool'
53
+ config.username = 'apiuser'
54
+ config.password = 'some_api_password'
55
+ config.adapter = :net_http
56
+ end
57
+ ```
58
+
59
+
60
+ Currently, only three methods are implemented:
61
+
62
+ #### Get Entire School Roster
63
+
64
+ ```ruby
65
+ roster = school_loop.get_roster
66
+ # returns a Nokogiri::XML::Document
67
+ ```
68
+
69
+ #### Get Teacher Roster
70
+
71
+ ```ruby
72
+ teacher_id = "12345"
73
+ roster = school_loop.get_roster(teacher_id)
74
+ # returns a Nokogiri::XML::Document (with a single 'teacher' node)
75
+ ```
76
+
77
+ Example roster data is available on [School Loop's Documentation](http://www.schoolloop.com/about-us/partners/progress-report-api/progress-report-api-schema/#ProgressReportSchema-IVSampleSchoolLoopRosterDownload)
78
+
79
+ #### Posting Progress Reports
80
+
81
+ For a detailed schema for Progress reports, you can look at [School Loop's Progress Report API Schema](http://www.schoolloop.com/about-us/partners/progress-report-api/progress-report-api-schema/)
82
+
83
+ ```ruby
84
+ prog_report_xml = <<-EOF
85
+ <?xml version="1.0" encoding="UTF-8"?>
86
+ <root>
87
+ <sections>
88
+ <section ID="1234">
89
+ <progressReports>
90
+ <progressReport markName="Current Grades" ... grade="A">
91
+ ...
92
+ </progressReport>
93
+ </progressReports>
94
+ </section>
95
+ </sections>
96
+ </root>
97
+ EOF
98
+
99
+ school_loop.publish_progress_report(prog_report_xml)
100
+ ```
101
+
102
+ ## License
103
+ [MIT](http://opensource.org/licenses/MIT)
104
+
105
+ ## Special Thanks
106
+ This gem is heavily based on the [bitbucket_rest_api](https://github.com/vongrippen/bitbucket) gem by Mike Cochran.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,25 @@
1
+ require 'school_loop/version'
2
+ require 'school_loop/configuration'
3
+ require 'school_loop/connection'
4
+ require 'school_loop/client'
5
+
6
+ module SchoolLoop
7
+ extend Configuration
8
+
9
+ class << self
10
+ def new(options = {}, &block)
11
+ SchoolLoop::Client.new(options, &block)
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ return super unless new.respond_to?(method)
16
+ new.send(method, *args, &block)
17
+ end
18
+
19
+ def respond_to?(method, include_private = false)
20
+ new.respond_to?(method, include_private) || super(method, include_private)
21
+ end
22
+ end
23
+
24
+
25
+ end
@@ -0,0 +1,39 @@
1
+ require 'school_loop/configuration'
2
+ require 'school_loop/connection'
3
+ require 'school_loop/resources'
4
+
5
+ module SchoolLoop
6
+ class Client
7
+ include Connection
8
+
9
+ include Resources::ProgressReports
10
+ include Resources::Roster
11
+
12
+ attr_reader *Configuration::VALID_OPTIONS_KEYS
13
+
14
+ # Callback to update global configuration options
15
+ class_eval do
16
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
17
+ define_method "#{key}=" do |arg|
18
+ self.instance_variable_set("@#{key}", arg)
19
+ end
20
+ end
21
+ end
22
+
23
+ # Creates new API
24
+ def initialize(options={}, &block)
25
+ super()
26
+ setup options
27
+
28
+ self.instance_eval(&block) if block_given?
29
+ end
30
+
31
+ def setup(options={})
32
+ options = SchoolLoop.options.merge(options)
33
+ Configuration::VALID_OPTIONS_KEYS.each do |key|
34
+ send("#{key}=", options[key])
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,54 @@
1
+ module SchoolLoop
2
+ module Configuration
3
+ VALID_OPTIONS_KEYS = [
4
+ :username,
5
+ :password,
6
+ :subdomain,
7
+
8
+ :secure,
9
+
10
+ :adapter,
11
+ :user_agent,
12
+ :connection_options,
13
+ :debug,
14
+ ].freeze
15
+
16
+ DEFAULT_ADAPTER = :net_http
17
+
18
+ DEFAULT_USERNAME = nil
19
+ DEFAULT_PASSWORD = nil
20
+ DEFAULT_SUBDOMAIN = nil
21
+
22
+ DEFAULT_USER_AGENT = "School Loop Ruby API v#{SchoolLoop::VERSION}".freeze
23
+
24
+ DEFAULT_CONNECTION_OPTIONS = {}
25
+
26
+ attr_accessor *VALID_OPTIONS_KEYS
27
+
28
+ def configure
29
+ yield self
30
+ end
31
+
32
+ def self.extended(base)
33
+ base.set_defaults
34
+ end
35
+
36
+ def options
37
+ options = { }
38
+ VALID_OPTIONS_KEYS.each { |k| options[k] = send(k) }
39
+ options
40
+ end
41
+
42
+ def set_defaults
43
+ self.adapter = DEFAULT_ADAPTER
44
+ self.user_agent = DEFAULT_USER_AGENT
45
+ self.connection_options = DEFAULT_CONNECTION_OPTIONS
46
+ self.username = DEFAULT_USERNAME
47
+ self.subdomain = DEFAULT_SUBDOMAIN
48
+ self.password = DEFAULT_PASSWORD
49
+ self.secure = true
50
+ self.debug = false
51
+ self
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'school_loop/response'
4
+ require 'school_loop/response/raise_error'
5
+ require 'school_loop/response/xmlize'
6
+
7
+ module SchoolLoop
8
+ module Connection
9
+ extend self
10
+
11
+
12
+ def connection
13
+ @connection ||= Faraday.new({url: "#{secure ? 'https' : 'http'}://#{subdomain}.schoolloop.com"}.merge(connection_options)) do |faraday|
14
+ faraday.request :basic_auth, username, password
15
+ faraday.headers[:user_agent] = user_agent
16
+
17
+ if debug
18
+ faraday.response :logger
19
+ end
20
+
21
+ faraday.use SchoolLoop::Response::Xmlize, :content_type => /\bxml$/
22
+ faraday.use SchoolLoop::Response::RaiseError
23
+ faraday.adapter adapter
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module SchoolLoop
2
+ module Error
3
+ class SchoolLoopError < StandardError
4
+ attr_reader :response_message, :response_headers
5
+
6
+ def initialize(message)
7
+ super message
8
+ @response_message = message
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ require "school_loop/error/service_error"
15
+ require "school_loop/error/bad_request"
16
+ require "school_loop/error/unauthorized"
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module SchoolLoop
4
+ module Error
5
+ # Raised when School Loop returns the HTTP status code 400
6
+ class BadRequest < ServiceError
7
+ def initialize(env)
8
+ super(env)
9
+ end
10
+ end
11
+ end
12
+ end # BitBucket
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ module SchoolLoop #:nodoc
4
+ # Raised when SchoolLoop returns any of the HTTP status codes
5
+ module Error
6
+ class ServiceError < SchoolLoopError
7
+ attr_accessor :http_headers, :http_status, :http_method, :url
8
+
9
+ def initialize(env)
10
+ super(generate_message(env))
11
+ @http_headers = env[:response_headers]
12
+ @http_status = env[:status]
13
+ @http_method = env[:method].to_s.upcase
14
+ @url = env[:url].to_s
15
+ end
16
+
17
+ def generate_message(env)
18
+ "#{env[:body]}"
19
+ end
20
+ end
21
+ end # Error
22
+ end # SchoolLoop
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module SchoolLoop #:nodoc
4
+ # Raised when SchoolLoop returns the HTTP status code 401
5
+ module Error
6
+ class Unauthorized < SchoolLoopError
7
+ def initialize(env)
8
+ super(env)
9
+ end
10
+ end
11
+ end # Error
12
+ end # SchoolLoop
@@ -0,0 +1,8 @@
1
+ module SchoolLoop
2
+ module Resources
3
+
4
+ end
5
+ end
6
+
7
+ require 'school_loop/resources/progress_reports'
8
+ require 'school_loop/resources/roster'
@@ -0,0 +1,15 @@
1
+ module SchoolLoop
2
+ module Resources
3
+ module ProgressReports
4
+
5
+ def publish_progress_report(xml_body)
6
+ connection.post do |req|
7
+ req.url "/api/progressReports"
8
+ req.headers['Content-Type'] = "application/xml"
9
+ req.body = xml_body
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module SchoolLoop
2
+ module Resources
3
+ module Roster
4
+
5
+ # Get the roster for all teachers, or limit to a specific teacher
6
+ def get_roster(teacher_id = nil)
7
+ connection.get do |req|
8
+ req.url "/api/roster"
9
+
10
+ if teacher_id
11
+ req.params['teacher_id'] = teacher_id
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+
2
+ require 'faraday'
3
+
4
+ module SchoolLoop
5
+ class Response < Faraday::Response::Middleware
6
+ CONTENT_TYPE = 'Content-Type'.freeze
7
+
8
+ class << self
9
+ attr_accessor :parser
10
+ end
11
+
12
+ def self.define_parser(&block)
13
+ @parser = block
14
+ end
15
+
16
+ def response_type(env)
17
+ env[:response_headers][CONTENT_TYPE].to_s
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ require 'faraday'
4
+ require 'school_loop/error'
5
+
6
+ module SchoolLoop
7
+ class Response::RaiseError < Faraday::Response::Middleware
8
+
9
+ def on_complete(env)
10
+ case env[:status].to_i
11
+ when 400
12
+ raise SchoolLoop::Error::BadRequest.new(env)
13
+ when 401
14
+ raise SchoolLoop::Error::Unauthorized.new(env)
15
+ when 400...600
16
+ raise SchoolLoop::Error::ServiceError.new(env)
17
+ end
18
+ end
19
+
20
+ end # Response::RaiseError
21
+ end # SchoolLoop
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'faraday'
4
+ require 'faraday_middleware'
5
+ require 'faraday_middleware/response_middleware'
6
+
7
+ module SchoolLoop
8
+ class Response::Xmlize < ::FaradayMiddleware::ResponseMiddleware
9
+ dependency 'nokogiri'
10
+
11
+ define_parser do |body|
12
+ ::Nokogiri::XML body
13
+ end
14
+
15
+ def parse(body)
16
+ case body
17
+ when ''
18
+ nil
19
+ when 'true'
20
+ true
21
+ when 'false'
22
+ false
23
+ else
24
+ self.class.parser.call body
25
+ end
26
+ end
27
+ end # Response::Xmlize
28
+ end # SchoolLoop
@@ -0,0 +1,3 @@
1
+ module SchoolLoop
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'school_loop/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "school_loop"
7
+ spec.version = SchoolLoop::VERSION
8
+ spec.platform = Gem::Platform::RUBY
9
+ spec.authors = ['Mitch Dempsey']
10
+ spec.email = ['mitch@mitchdempsey.com']
11
+ spec.licenses = ['MIT']
12
+ spec.homepage = "https://github.com/webdestroya/school_loop"
13
+ spec.summary = "Client for School Loop API (OpenLoop)"
14
+ spec.description = "Client for School Loop API (OpenLoop)"
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.0'
22
+
23
+ spec.add_dependency 'faraday', '~> 0.9'
24
+ spec.add_dependency 'faraday_middleware', '~> 0.9'
25
+ #spec.add_dependency 'ox', '~> 2.2.1'
26
+ spec.add_dependency 'nokogiri', '>= 1.5.2'
27
+ end
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <root>
3
+ <sections>
4
+ <section ID="1234">
5
+ <progressReports>
6
+ <progressReport markName="Current Grades" date="2007-08-12T22:24:15Z" score="0.95" grade="A" hasScore="true" useWeighting="false" gradebookDate="">
7
+ <gradeDefinitions>
8
+ <keyValue key="INC" value="INCOMPLETE (not handed in)"/>
9
+ </gradeDefinitions>
10
+ <categories>
11
+ <category hasScore="true" name="Assignment" weight="-1.000" score="0.000"/>
12
+ </categories>
13
+ <course period="3" systemID="100000881299" name="English"/>
14
+ <gradingScale>
15
+ <cutoffs>
16
+ <cutoff Start="90" Name="A"/>
17
+ <cutoff Start="0" Name="F"/>
18
+ </cutoffs>
19
+ </gradingScale>
20
+ <grades>
21
+ <grade score="10" excused="false" comment="" grade="B">
22
+ <assignment categoryName="Assignment" maxPoints="10" dueDate="2007-05-18T00:00:00Z" title="WWI"/>
23
+ </grade>
24
+ <grade score="10" excused="false" comment="" grade="B">
25
+ <assignment categoryName="Assignment" maxPoints="10" dueDate="2007-05-18T00:00:00Z" title="Thing 2"/>
26
+ </grade>
27
+ </grades>
28
+ <student name="Cleaver, Kevin" schoolID="7123456"/>
29
+ </progressReport>
30
+ </progressReports>
31
+ </section>
32
+ </sections>
33
+ </root>
@@ -0,0 +1,50 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <root>
4
+ <roster source="schoolloop.com">
5
+ <teachers>
6
+ <teacher systemID="1111180131503" localID="111111" name="Reynolds, Mr. Malcom">
7
+ <courses>
8
+ <course systemID="1111107012313" name="Math">
9
+ <periods>
10
+ <period systemID="1111133312598" number="1">
11
+ <assignments/>
12
+ <students>
13
+ <student permID="1000265" systemID="1111170083336" gradeLevel="12" firstName="Ryan" middleName="Chris" lastName="Test" displayID="1000265" localID="1234" sectionID="1234" sectionSystemID="1111133311259"/>
14
+ <student permID="1000186" systemID="1111170083213" gradeLevel="11" firstName="Manuel" middleName="" lastName="Test" displayID="1000186" localID="5677" sectionID="1234" sectionSystemID="1111133311259"/>
15
+ <student permID="1000673" systemID="1111170083137" gradeLevel="11" firstName="Justin" middleName="" lastName="Test" displayID="1000673" localID="8900" sectionID="1234" sectionSystemID="1111133311259"/>
16
+ <student permID="1000878" systemID="1111170082883" gradeLevel="11" firstName="Dave" middleName="" lastName="Test" displayID="1000878" localID="1235" sectionID="1234" sectionSystemID="1111133311259"/>
17
+ <student permID="1000985" systemID="1111107494444" gradeLevel="12" firstName="Colby" middleName="Jeffrey" lastName="Test" displayID="1000985" localID="1236" sectionID="1234" sectionSystemID="1111133311259"/>
18
+ </students>
19
+ </period>
20
+ </periods>
21
+ </course>
22
+ <course systemID="1111107012069" name="English 3">
23
+ <periods>
24
+ <period systemID="1111133312700" number="5">
25
+ <assignments>
26
+ <assignment dueDate="2015-08-27T00:00:00Z" maxPoints="10.0" systemID="1111143710574" categoryName="Assignment" title="Parent Signature" categoryID="1111146128059"/>
27
+ <assignment dueDate="2015-09-10T00:00:00Z" maxPoints="42.0" systemID="1111124172130" categoryName="Assignment" title="Unit 1 Literary Terms Worksheet and Test" categoryID="1111146128059"/>
28
+ <assignment dueDate="2015-10-02T00:00:00Z" maxPoints="100.0" systemID="1111124171925" categoryName="Assignment" title="Participation/Professionalism" categoryID="1111146128059"/>
29
+ </assignments>
30
+ <students>
31
+ <student permID="1000338" systemID="1111170083144" gradeLevel="11" firstName="Henny" middleName="" lastName="Test" displayID="1000338" localID="123" sectionID="1000" sectionSystemID="1111133311919"/>
32
+ <student permID="1000997" systemID="1111170083020" gradeLevel="11" firstName="Sean" middleName="" lastName="Test" displayID="1000997" localID="456" sectionID="1000" sectionSystemID="1111133311919"/>
33
+ <student permID="1000673" systemID="1111170083137" gradeLevel="11" firstName="Justin" middleName="" lastName="Test" displayID="1000673" localID="789" sectionID="1000" sectionSystemID="1111133311919"/>
34
+ <student permID="1000828" systemID="1111143756226" gradeLevel="11" firstName="Fred" middleName="Adele" lastName="Test" displayID="1000828" localID="8456" sectionID="1000" sectionSystemID="1111133311919"/>
35
+ <student permID="1000977" systemID="1111170082757" gradeLevel="11" firstName="Peter" middleName="Jay" lastName="Test" displayID="1000977" localID="9456" sectionID="1000" sectionSystemID="1111133311919"/>
36
+ <student permID="1000183" systemID="1111170083015" gradeLevel="11" firstName="Kyle" middleName="Kend" lastName="Test" displayID="1000183" localID="7456" sectionID="1000" sectionSystemID="1111133311919"/>
37
+ </students>
38
+ </period>
39
+ </periods>
40
+ </course>
41
+ </courses>
42
+ <categories>
43
+ <category systemID="1111146879613" name="Classwork - Homework"/>
44
+ <category systemID="1111146128059" name="Assignment"/>
45
+ <category systemID="1111175461981" name="Quiz / Test"/>
46
+ </categories>
47
+ </teacher>
48
+ </teachers>
49
+ </roster>
50
+ </root>
data/spec/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ if RUBY_ENGINE == 'ruby'
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+ SimpleCov.start
10
+ end
11
+
12
+ require 'school_loop'
13
+ require 'rspec'
14
+ require 'webmock/rspec'
15
+
16
+ WebMock.disable_net_connect!(:allow => 'coveralls.io')
17
+
18
+ RSpec.configure do |config|
19
+ config.raise_errors_for_deprecations!
20
+ end
21
+
22
+ def fixture_path
23
+ File.expand_path("../fixtures", __FILE__)
24
+ end
25
+
26
+ def fixture(file)
27
+ File.new(fixture_path + '/' + file)
28
+ end
@@ -0,0 +1,84 @@
1
+ require 'helper'
2
+
3
+ describe SchoolLoop::Client do
4
+
5
+
6
+
7
+
8
+ describe "error handling" do
9
+
10
+ let(:unauth_client) do
11
+ SchoolLoop.new do |config|
12
+ config.subdomain = 'subdomain'
13
+ config.username = 'username'
14
+ config.password = 'password'
15
+ config.adapter = :net_http
16
+ end
17
+ end
18
+
19
+ # bad user, good password
20
+ # 401 ERROR: Authorization failed: user or role not privileged.
21
+
22
+ # bad user, bad password
23
+ # 401 ERROR: Authorization failed: password incorrect.
24
+
25
+ # api not enabled
26
+ # 400 Error: OpenLoop is not enabled for subdomain.schoolloop.com. Contact School Loop.
27
+
28
+
29
+ context "401 Unauthorized" do
30
+ before(:each) do
31
+ stub_request(:any, /.*schoolloop\.com\/.*/)
32
+ .to_return(status: 401, body: "ERROR: Authorization failed: password incorrect.")
33
+ end
34
+ it "raises an exception" do
35
+ expect { unauth_client.get_roster }.to raise_error(SchoolLoop::Error::Unauthorized)
36
+ end
37
+ end
38
+
39
+ context "400 Bad Request" do
40
+ before(:each) do
41
+ stub_request(:any, /.*schoolloop\.com\/.*/)
42
+ .to_return(status: 400, body: "some error message")
43
+ end
44
+
45
+ it "raises an exception" do
46
+ expect { unauth_client.get_roster }.to raise_error(SchoolLoop::Error::BadRequest, "some error message")
47
+ end
48
+ end
49
+
50
+ context "404" do
51
+ before(:each) do
52
+ stub_request(:any, /.*schoolloop\.com\/.*/)
53
+ .to_return(status: 404, body: "not found")
54
+ end
55
+
56
+ it "raises an exception" do
57
+ expect { unauth_client.get_roster }.to raise_error(SchoolLoop::Error::ServiceError, "not found")
58
+ end
59
+ end
60
+
61
+ context "503" do
62
+ before(:each) do
63
+ stub_request(:any, /.*schoolloop\.com\/.*/)
64
+ .to_return(status: 503, body: "backend failure")
65
+ end
66
+
67
+ it "raises an exception" do
68
+ expect { unauth_client.get_roster }.to raise_error(SchoolLoop::Error::ServiceError, "backend failure")
69
+ end
70
+ end
71
+
72
+ context "timeout" do
73
+ before(:each) do
74
+ stub_request(:any, /.*schoolloop\.com\/.*/).to_timeout
75
+ end
76
+
77
+ it "raises an exception" do
78
+ expect { unauth_client.get_roster }.to raise_error(Faraday::TimeoutError)
79
+ end
80
+ end
81
+
82
+
83
+ end
84
+ end
@@ -0,0 +1,41 @@
1
+ require 'helper'
2
+
3
+ describe SchoolLoop::Configuration do
4
+
5
+ describe ".configure" do
6
+
7
+ it "sets global configuration settings" do
8
+ SchoolLoop.configure do |config|
9
+ config.username = "test2"
10
+ config.password = "setting2"
11
+ end
12
+
13
+ expect(SchoolLoop.options[:username]).to eq "test2"
14
+ expect(SchoolLoop.options[:password]).to eq "setting2"
15
+
16
+ client = SchoolLoop.new
17
+
18
+ expect(client.username).to eq "test2"
19
+ expect(client.password).to eq "setting2"
20
+ end
21
+
22
+ it "allows the user to specify extra config options" do
23
+ SchoolLoop.configure do |config|
24
+ config.connection_options = {ssl: {verify: false}, request: {timeout: 1234}}
25
+ end
26
+
27
+ client = SchoolLoop.new
28
+ expect(client.connection.options.timeout).to eq 1234
29
+ end
30
+
31
+ describe ":secure" do
32
+ it { expect(SchoolLoop.new(secure: false, subdomain: "test").connection.url_prefix.scheme).to eq "http" }
33
+
34
+ it { expect(SchoolLoop.new(secure: true, subdomain: "test").connection.url_prefix.scheme).to eq "https" }
35
+ it { expect(SchoolLoop.new(subdomain: "test").connection.url_prefix.scheme).to eq "https" }
36
+ end
37
+
38
+ end
39
+
40
+
41
+ end
@@ -0,0 +1,39 @@
1
+ require 'helper'
2
+
3
+ describe SchoolLoop::Resources::ProgressReports do
4
+
5
+ let(:client) do
6
+ SchoolLoop.new do |config|
7
+ config.subdomain = 'subdomain'
8
+ config.username = 'username'
9
+ config.password = 'password'
10
+ config.adapter = :net_http
11
+ end
12
+ end
13
+
14
+ describe "#publish_progress_report" do
15
+
16
+ it "publishes a progress report" do
17
+ stub_request(:post, "https://username:password@subdomain.schoolloop.com/api/progressReports")
18
+ .to_return(status: 200, body: "SUCCESS\n")
19
+
20
+ response = client.publish_progress_report("<xml/>")
21
+ expect(a_request(:post, /.*subdomain\.schoolloop\.com\/api\/progressReports/)).to have_been_made.once
22
+
23
+ expect(response.status).to eq 200
24
+ expect(response.body).to eq "SUCCESS\n"
25
+ end
26
+
27
+ context "errors" do
28
+ before(:each) do
29
+ stub_request(:any, /.*schoolloop\.com\/.*/)
30
+ .to_return(status: 400, body: "Error: failed to parse XML reports. No reports found. Check XML syntax.")
31
+ end
32
+
33
+
34
+ it { expect { client.publish_progress_report("<xml/>") }.to raise_error(SchoolLoop::Error::BadRequest) }
35
+ it { expect { client.publish_progress_report("<xml/>") }.to raise_error("Error: failed to parse XML reports. No reports found. Check XML syntax.") }
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ require 'helper'
2
+
3
+ describe SchoolLoop::Resources::Roster do
4
+
5
+ let(:client) do
6
+ SchoolLoop.new do |config|
7
+ config.subdomain = 'subdomain'
8
+ config.username = 'username'
9
+ config.password = 'password'
10
+ config.adapter = :net_http
11
+ end
12
+ end
13
+
14
+ describe "#get_roster" do
15
+
16
+ before do
17
+ stub_request(:get, /.*schoolloop\.com\/api\/roster.*/)
18
+ .to_return(status: 200, body: fixture("roster_response.xml").read,
19
+ headers: {'Content-Type' => 'text/xml'}
20
+ )
21
+ end
22
+
23
+ it "for entire school" do
24
+ response = client.get_roster
25
+ expect(a_request(:get, /.*subdomain\.schoolloop\.com\/api\/roster/)).to have_been_made.once
26
+
27
+ expect(response.status).to eq 200
28
+ expect(response.body).to be_a Nokogiri::XML::Document
29
+ expect(response.body.xpath("//root/roster/teachers/teacher")[0].attr("name")).to eq "Reynolds, Mr. Malcom"
30
+ end
31
+
32
+ it "for a specific teacher" do
33
+ response = client.get_roster("12345")
34
+ expect(a_request(:get, /.*subdomain\.schoolloop\.com\/api\/roster/).with(query: {teacher_id: "12345"})).to have_been_made.once
35
+
36
+ expect(response.status).to eq 200
37
+ expect(response.body).to be_a Nokogiri::XML::Document
38
+ expect(response.body.xpath("//root/roster/teachers/teacher")[0].attr("name")).to eq "Reynolds, Mr. Malcom"
39
+ end
40
+ end
41
+ end
File without changes
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+
3
+ describe SchoolLoop do
4
+
5
+ describe ".new" do
6
+
7
+ it "allows an instance to override the global configuration" do
8
+ SchoolLoop.configure do |config|
9
+ config.username = "test2"
10
+ config.password = "setting2"
11
+ end
12
+
13
+ client = SchoolLoop.new
14
+
15
+ expect(client.username).to eq "test2"
16
+ expect(client.password).to eq "setting2"
17
+
18
+ client2 = SchoolLoop.new(username: "test3", password: "setting3")
19
+ expect(client2.username).to eq "test3"
20
+ expect(client2.password).to eq "setting3"
21
+
22
+ expect(client.username).to eq "test2"
23
+ expect(client.password).to eq "setting2"
24
+
25
+ client3 = SchoolLoop.new(username: "test4")
26
+ expect(client3.username).to eq "test4"
27
+ end
28
+
29
+ it "does not modify the global configuration" do
30
+ SchoolLoop.configure do |config|
31
+ config.username = "testg"
32
+ config.password = "passg"
33
+ end
34
+
35
+ client = SchoolLoop.new(username: "gtest", password: "gpass")
36
+
37
+ expect(client.username).to eq "gtest"
38
+ expect(client.password).to eq "gpass"
39
+
40
+ expect(SchoolLoop.options[:username]).to eq "testg"
41
+ expect(SchoolLoop.options[:password]).to eq "passg"
42
+ end
43
+ end
44
+
45
+
46
+ describe ".method_missing" do
47
+ let(:client_dbl) { double(SchoolLoop::Client) }
48
+
49
+ before do
50
+ allow(SchoolLoop).to receive(:new).and_return(client_dbl)
51
+ end
52
+
53
+ it "creates an instance and calls the method" do
54
+ expect(client_dbl).to receive(:get_roster).with("1234").and_return(true)
55
+ expect(SchoolLoop.get_roster("1234")).to be_truthy
56
+ end
57
+ end
58
+
59
+ describe ".respond_to?" do
60
+ it { expect(SchoolLoop).to respond_to(:get_roster) }
61
+ it { expect(SchoolLoop).to respond_to(:publish_progress_report) }
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: school_loop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mitch Dempsey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faraday_middleware
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.5.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.5.2
69
+ description: Client for School Loop API (OpenLoop)
70
+ email:
71
+ - mitch@mitchdempsey.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".ruby-version"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE
83
+ - README.md
84
+ - Rakefile
85
+ - lib/school_loop.rb
86
+ - lib/school_loop/client.rb
87
+ - lib/school_loop/configuration.rb
88
+ - lib/school_loop/connection.rb
89
+ - lib/school_loop/error.rb
90
+ - lib/school_loop/error/bad_request.rb
91
+ - lib/school_loop/error/service_error.rb
92
+ - lib/school_loop/error/unauthorized.rb
93
+ - lib/school_loop/resources.rb
94
+ - lib/school_loop/resources/progress_reports.rb
95
+ - lib/school_loop/resources/roster.rb
96
+ - lib/school_loop/response.rb
97
+ - lib/school_loop/response/raise_error.rb
98
+ - lib/school_loop/response/xmlize.rb
99
+ - lib/school_loop/version.rb
100
+ - school_loop.gemspec
101
+ - spec/fixtures/progress_report_submission.xml
102
+ - spec/fixtures/roster_response.xml
103
+ - spec/helper.rb
104
+ - spec/school_loop/client_spec.rb
105
+ - spec/school_loop/configuration_spec.rb
106
+ - spec/school_loop/resources/progress_reports_spec.rb
107
+ - spec/school_loop/resources/roster_spec.rb
108
+ - spec/school_loop/response/xmlize_spec.rb
109
+ - spec/school_loop_spec.rb
110
+ homepage: https://github.com/webdestroya/school_loop
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.4.5
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Client for School Loop API (OpenLoop)
134
+ test_files:
135
+ - spec/fixtures/progress_report_submission.xml
136
+ - spec/fixtures/roster_response.xml
137
+ - spec/helper.rb
138
+ - spec/school_loop/client_spec.rb
139
+ - spec/school_loop/configuration_spec.rb
140
+ - spec/school_loop/resources/progress_reports_spec.rb
141
+ - spec/school_loop/resources/roster_spec.rb
142
+ - spec/school_loop/response/xmlize_spec.rb
143
+ - spec/school_loop_spec.rb