school_loop 0.0.1

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