academical-api-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9f12096992031836a22c3e9f50a22ab4f3b93235
4
+ data.tar.gz: 44b0dd7aca9efa9a9f3b3957a5e8e0bc851ebe01
5
+ SHA512:
6
+ metadata.gz: 0d0bbfcee9b30be69eac31dfcadba5f29365a020767e136b593196168e83fa697ff497396635e211db68182fbfc4b3e8b6da83f5a83375b9fe7b107b2fdc9cd9
7
+ data.tar.gz: b951d46ee97b50c9628d1249821f76eb09dfae5d6c4a80b0465e0fed5247677c137965f037e3bf029c3a581d47fe5e03b4899e153805d58750b689625f447666
@@ -0,0 +1,8 @@
1
+ require "rubygems"
2
+
3
+ require "academical/client"
4
+ require "academical/error"
5
+ require "academical/http_client"
6
+
7
+ module Academical
8
+ end
@@ -0,0 +1,135 @@
1
+ module Academical
2
+
3
+ module Api
4
+
5
+ # All schools that are using Academical
6
+ class Schools
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Returns a school object given its ID
13
+ #
14
+ # '/schools/:id' GET
15
+ #
16
+ # id - ID of the school
17
+ def get(id, options = {})
18
+ body = options.fetch(:query, {})
19
+
20
+ @client.get("/schools/#{id}", body, options)
21
+ end
22
+
23
+ # Delete a school from the API given its ID
24
+ #
25
+ # '/schools/:id' DELETE
26
+ #
27
+ # id - ID of the school
28
+ def delete(id, options = {})
29
+ body = options.fetch(:body, {})
30
+
31
+ @client.delete("/schools/#{id}", body, options)
32
+ end
33
+
34
+ # Returns a list of schedules that have been created in a given school
35
+ #
36
+ # '/schools/:id/schedules' GET
37
+ #
38
+ # id - ID of the school
39
+ def schedules(id, options = {})
40
+ body = options.fetch(:query, {})
41
+
42
+ @client.get("/schools/#{id}/schedules", body, options)
43
+ end
44
+
45
+ # Returns a list of students for a given school
46
+ #
47
+ # '/schools/:id/students' GET
48
+ #
49
+ # id - ID of the school
50
+ def students(id, options = {})
51
+ body = options.fetch(:query, {})
52
+
53
+ @client.get("/schools/#{id}/students", body, options)
54
+ end
55
+
56
+ # Returns a list of sections for a given school
57
+ #
58
+ # '/schools/:id/sections' GET
59
+ #
60
+ # id - ID of the school
61
+ def sections(id, options = {})
62
+ body = options.fetch(:query, {})
63
+
64
+ @client.get("/schools/#{id}/sections", body, options)
65
+ end
66
+
67
+ # Return a list of teachers for a given school
68
+ #
69
+ # '/schools/:id/teachers' GET
70
+ #
71
+ # id - ID of the school
72
+ def teachers(id, options = {})
73
+ body = options.fetch(:query, {})
74
+
75
+ @client.get("/schools/#{id}/teachers", body, options)
76
+ end
77
+
78
+ # Creates a new school object in Academical. All required parameters must be provided
79
+ #
80
+ # '/schools/' POST
81
+ #
82
+ # name - Name of the school
83
+ # nickname - Nickname of the school
84
+ # locale - Locale of the school
85
+ # timezone - Timezone of the school
86
+ # departments - A list of Department objects. Each department should specify a name and faculty_name
87
+ # identity_providers - A list of Identify Providers to use with this school
88
+ # app_ui - Object describing UI specific parameters for our planner app
89
+ # terms - A list containing Term objects. Each term should define a name, a start date and end date.
90
+ def create(name, nickname, locale, timezone, departments, identity_providers, app_ui, terms, options = {})
91
+ body = options.fetch(:body, {})
92
+ body[:name] = name
93
+ body[:nickname] = nickname
94
+ body[:locale] = locale
95
+ body[:timezone] = timezone
96
+ body[:departments] = departments
97
+ body[:identity_providers] = identity_providers
98
+ body[:app_ui] = app_ui
99
+ body[:terms] = terms
100
+
101
+ @client.post("/schools/", body, options)
102
+ end
103
+
104
+ # Update a new school in Academical. You must provide all of the required parameters
105
+ #
106
+ # '/schools/:id/' PUT
107
+ #
108
+ # id - Generated ID for the school
109
+ # name - Name of the school
110
+ # nickname - Nickname of the school
111
+ # locale - Locale of the school
112
+ # timezone - Timezone of the school
113
+ # departments - A list of Department objects. Each department should specify a name and faculty_name
114
+ # identity_providers - A list of Identify Providers to use with this school
115
+ # app_ui - Object describing UI specific parameters for our planner app
116
+ # terms - A list containing Term objects. Each term should define a name, a start date and end date.
117
+ def update(id, name, nickname, locale, timezone, departments, identity_providers, app_ui, terms, options = {})
118
+ body = options.fetch(:body, {})
119
+ body[:name] = name
120
+ body[:nickname] = nickname
121
+ body[:locale] = locale
122
+ body[:timezone] = timezone
123
+ body[:departments] = departments
124
+ body[:identity_providers] = identity_providers
125
+ body[:app_ui] = app_ui
126
+ body[:terms] = terms
127
+
128
+ @client.put("/schools/#{id}/", body, options)
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+
135
+ end
@@ -0,0 +1,121 @@
1
+ module Academical
2
+
3
+ module Api
4
+
5
+ # Represents the section of a course in a school
6
+ class Sections
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Returns a section object given its ID
13
+ #
14
+ # '/sections/:id' GET
15
+ #
16
+ # id - ID of the section
17
+ def get(id, options = {})
18
+ body = options.fetch(:query, {})
19
+
20
+ @client.get("/sections/#{id}", body, options)
21
+ end
22
+
23
+ # Delete a section given its ID
24
+ #
25
+ # '/sections/:id' DELETE
26
+ #
27
+ # id - ID of the section
28
+ def delete(id, options = {})
29
+ body = options.fetch(:body, {})
30
+
31
+ @client.delete("/sections/#{id}", body, options)
32
+ end
33
+
34
+ # Creates a new section object in Academical. All required parameters must be provided.
35
+ #
36
+ # '/sections/' POST
37
+ #
38
+ # course_name - Name of the course
39
+ # course_code - The code of the course which this section belongs to
40
+ # section_number - Section number of the course. This is X of Y sections for course K
41
+ # section_id - Unique identifier for the section
42
+ # term - The term object this section will be taking place in
43
+ # seats - Object describing the number of available, taken and total seats for the section
44
+ # school_id - ID of the school this section belongs to
45
+ # events - A list of events this section takes place on
46
+ # departments - List of departments this section belongs to.
47
+ # credits - Number of credits this course is worth
48
+ def create(course_name, course_code, section_number, section_id, term, seats, school_id, events, departments, credits, options = {})
49
+ body = options.fetch(:body, {})
50
+ body[:course_name] = course_name
51
+ body[:course_code] = course_code
52
+ body[:section_number] = section_number
53
+ body[:section_id] = section_id
54
+ body[:term] = term
55
+ body[:seats] = seats
56
+ body[:school_id] = school_id
57
+ body[:events] = events
58
+ body[:departments] = departments
59
+ body[:credits] = credits
60
+
61
+ @client.post("/sections/", body, options)
62
+ end
63
+
64
+ # Update the attributes of a section given its ID as a part of the body of the request
65
+ #
66
+ # '/sections/:id' PUT
67
+ #
68
+ # id - ID of the section
69
+ # course_name - Name of the course
70
+ # course_code - The code of the course which this section belongs to
71
+ # section_number - Section number of the course. This is X of Y sections for course K
72
+ # section_id - Unique identifier for the section
73
+ # term - The term object this section will be taking place in
74
+ # seats - Object describing the number of available, taken and total seats for the section
75
+ # school_id - ID of the school this section belongs to
76
+ # events - A list of events this section takes place on
77
+ # departments - List of departments this section belongs to.
78
+ # credits - Number of credits this course is worth
79
+ def update(id, course_name, course_code, section_number, section_id, term, seats, school_id, events, departments, credits, options = {})
80
+ body = options.fetch(:body, {})
81
+ body[:course_name] = course_name
82
+ body[:course_code] = course_code
83
+ body[:section_number] = section_number
84
+ body[:section_id] = section_id
85
+ body[:term] = term
86
+ body[:seats] = seats
87
+ body[:school_id] = school_id
88
+ body[:events] = events
89
+ body[:departments] = departments
90
+ body[:credits] = credits
91
+
92
+ @client.put("/sections/#{id}", body, options)
93
+ end
94
+
95
+ # Returns the list of teachers given a section ID
96
+ #
97
+ # '/sections/:id/teachers' GET
98
+ #
99
+ # id - ID of the section
100
+ def teachers(id, options = {})
101
+ body = options.fetch(:query, {})
102
+
103
+ @client.get("/sections/#{id}/teachers", body, options)
104
+ end
105
+
106
+ # List of all the schedules with a given section
107
+ #
108
+ # '/sections/:id/schedules' GET
109
+ #
110
+ # id - ID of the section
111
+ def schedules(id, options = {})
112
+ body = options.fetch(:query, {})
113
+
114
+ @client.get("/sections/#{id}/schedules", body, options)
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,68 @@
1
+ module Academical
2
+
3
+ module Api
4
+
5
+ # A teacher in a school.
6
+ class Teachers
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ # Get a teacher given its ID
13
+ #
14
+ # '/teachers/:id' GET
15
+ #
16
+ # id - ID of the teacher
17
+ def get(id, options = {})
18
+ body = options.fetch(:query, {})
19
+
20
+ @client.get("/teachers/#{id}", body, options)
21
+ end
22
+
23
+ # Delete a teacher given its ID
24
+ #
25
+ # '/teachers/:id' DELETE
26
+ #
27
+ # id - ID of the teacher
28
+ def delete(id, options = {})
29
+ body = options.fetch(:body, {})
30
+
31
+ @client.delete("/teachers/#{id}", body, options)
32
+ end
33
+
34
+ # Create a new teacher in Academical. You must provide all of the required values
35
+ #
36
+ # '/teachers/' POST
37
+ #
38
+ # name - Object with the name of the teacher
39
+ # school_id - ID of the school which this teacher belongs to
40
+ def create(name, school_id, options = {})
41
+ body = options.fetch(:body, {})
42
+ body[:name] = name
43
+ body[:school_id] = school_id
44
+
45
+ @client.post("/teachers/", body, options)
46
+ end
47
+
48
+ # Update a teacher in Academical. You must provide all of the required parameters
49
+ #
50
+ # '/teachers/:id' PUT
51
+ #
52
+ # id - ID of the teacher
53
+ # name - Object with the name of the teacher
54
+ # school_id - ID of the school which this teacher belongs to
55
+ def update(id, name, school_id, options = {})
56
+ body = options.fetch(:body, {})
57
+ body[:id] = id
58
+ body[:name] = name
59
+ body[:school_id] = school_id
60
+
61
+ @client.put("/teachers/#{id}", body, options)
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,33 @@
1
+ require "faraday"
2
+ require "json"
3
+
4
+ require "academical/api/schools"
5
+ require "academical/api/sections"
6
+ require "academical/api/teachers"
7
+
8
+ module Academical
9
+
10
+ class Client
11
+
12
+ def initialize(auth = {}, options = {})
13
+ @http_client = Academical::HttpClient::HttpClient.new(auth, options)
14
+ end
15
+
16
+ # All schools that are using Academical
17
+ def schools()
18
+ Academical::Api::Schools.new(@http_client)
19
+ end
20
+
21
+ # Represents the section of a course in a school
22
+ def sections()
23
+ Academical::Api::Sections.new(@http_client)
24
+ end
25
+
26
+ # A teacher in a school.
27
+ def teachers()
28
+ Academical::Api::Teachers.new(@http_client)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,9 @@
1
+ require "academical/error/client_error"
2
+
3
+ module Academical
4
+
5
+ module Error
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,18 @@
1
+ module Academical
2
+
3
+ module Error
4
+
5
+ class ClientError < ::StandardError
6
+
7
+ attr_reader :code
8
+
9
+ def initialize(message, code)
10
+ @code = code
11
+ super message
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,122 @@
1
+ require "academical/http_client/auth_handler"
2
+ require "academical/http_client/error_handler"
3
+ require "academical/http_client/request_handler"
4
+ require "academical/http_client/response"
5
+ require "academical/http_client/response_handler"
6
+
7
+ module Academical
8
+
9
+ module HttpClient
10
+
11
+ # Main HttpClient which is used by Api classes
12
+ class HttpClient
13
+
14
+ attr_accessor :options, :headers
15
+
16
+ def initialize(auth = {}, options = {})
17
+
18
+ if auth.is_a?(String)
19
+ auth = { :http_header => auth }
20
+ end
21
+
22
+ @options = {
23
+ :base => "https://api.academical.co",
24
+ :user_agent => "alpaca/0.2.1 (https://github.com/pksunkara/alpaca)"
25
+ }
26
+
27
+ @options.update(options)
28
+
29
+ @headers = {
30
+ "user-agent" => @options[:user_agent]
31
+ }
32
+
33
+ if @options.has_key?(:headers)
34
+ @headers.update(Hash[@options[:headers].map { |k, v| [k.downcase, v] }])
35
+ @options.delete(:headers)
36
+ end
37
+
38
+ @client = Faraday.new(@options[:base]) do |conn|
39
+ conn.use(Academical::HttpClient::AuthHandler, auth)
40
+ conn.use(Academical::HttpClient::ErrorHandler)
41
+
42
+ conn.adapter(Faraday.default_adapter)
43
+ end
44
+ end
45
+
46
+ def get(path, params = {}, options = {})
47
+ request(path, nil, "get", options.merge({ :query => params }))
48
+ end
49
+
50
+ def post(path, body = {}, options = {})
51
+ request(path, body, "post", options)
52
+ end
53
+
54
+ def patch(path, body = {}, options = {})
55
+ request(path, body, "patch", options)
56
+ end
57
+
58
+ def delete(path, body = {}, options = {})
59
+ request(path, body, "delete", options)
60
+ end
61
+
62
+ def put(path, body = {}, options = {})
63
+ request(path, body, "put", options)
64
+ end
65
+
66
+ # Intermediate function which does three main things
67
+ #
68
+ # - Transforms the body of request into correct format
69
+ # - Creates the requests with give parameters
70
+ # - Returns response body after parsing it into correct format
71
+ def request(path, body, method, options)
72
+ options = @options.merge(options)
73
+
74
+ options[:headers] = options[:headers] || {}
75
+ options[:headers] = @headers.merge(Hash[options[:headers].map { |k, v| [k.downcase, v] }])
76
+
77
+ options[:body] = body
78
+
79
+ if method != "get"
80
+ options[:body] = options[:body] || {}
81
+ options = set_body(options)
82
+ end
83
+
84
+ response = create_request(method, path, options)
85
+
86
+ body = get_body(response)
87
+
88
+ Academical::HttpClient::Response.new(body, response.status, response.headers)
89
+ end
90
+
91
+ # Creating a request with the given arguments
92
+ #
93
+ # If api_version is set, appends it immediately after host
94
+ def create_request(method, path, options)
95
+ version = options.has_key?(:api_version) ? "/#{options[:api_version]}" : ""
96
+
97
+ path = "#{version}#{path}"
98
+
99
+ instance_eval <<-RUBY, __FILE__, __LINE__ + 1
100
+ @client.#{method}(path) do |req|
101
+ req.body = options[:body]
102
+ req.headers.update(options[:headers])
103
+ req.params.update(options[:query]) if options[:query]
104
+ end
105
+ RUBY
106
+ end
107
+
108
+ # Get response body in correct format
109
+ def get_body(response)
110
+ Academical::HttpClient::ResponseHandler.get_body(response)
111
+ end
112
+
113
+ # Set request body in correct format
114
+ def set_body(options)
115
+ Academical::HttpClient::RequestHandler.set_body(options)
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end
@@ -0,0 +1,73 @@
1
+ require "base64"
2
+
3
+ module Academical
4
+
5
+ module HttpClient
6
+
7
+ # AuthHandler takes care of devising the auth type and using it
8
+ class AuthHandler < Faraday::Middleware
9
+
10
+ HTTP_HEADER = 1
11
+
12
+ def initialize(app, auth = {}, options = {})
13
+ @auth = auth
14
+ super(app)
15
+ end
16
+
17
+ def call(env)
18
+ if !@auth.empty?
19
+ auth = get_auth_type
20
+ flag = false
21
+
22
+ if auth == HTTP_HEADER
23
+ env = http_header(env)
24
+ flag = true
25
+ end
26
+
27
+ if !flag
28
+ raise StandardError.new "Unable to calculate authorization method. Please check"
29
+ end
30
+ else
31
+ raise StandardError.new "Server requires authentication to proceed further. Please check"
32
+ end
33
+
34
+ @app.call(env)
35
+ end
36
+
37
+ # Calculating the Authentication Type
38
+ def get_auth_type()
39
+
40
+ if @auth.has_key?(:http_header)
41
+ return HTTP_HEADER
42
+ end
43
+
44
+ return -1
45
+ end
46
+
47
+ # Authorization with HTTP header
48
+ def http_header(env)
49
+ env[:request_headers]["Authorization"] = "Bearer #{@auth[:http_header]}"
50
+
51
+ return env
52
+ end
53
+
54
+ def query_params(url)
55
+ if url.query.nil? or url.query.empty?
56
+ {}
57
+ else
58
+ Faraday::Utils.parse_query(url.query)
59
+ end
60
+ end
61
+
62
+ def merge_query(env, query)
63
+ query = query.update query_params(env[:url])
64
+
65
+ env[:url].query = Faraday::Utils.build_query(query)
66
+
67
+ return env
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,51 @@
1
+ module Academical
2
+
3
+ module HttpClient
4
+
5
+ # ErrorHanlder takes care of selecting the error message from response body
6
+ class ErrorHandler < Faraday::Middleware
7
+
8
+ def initialize(app)
9
+ super(app)
10
+ end
11
+
12
+ def call(env)
13
+ @app.call(env).on_complete do |env|
14
+ code = env[:response].status
15
+ type = env[:response].headers["content-type"]
16
+
17
+ case code
18
+ when 500...599
19
+ raise Academical::Error::ClientError.new("Error #{code}", code)
20
+ when 400...499
21
+ body = Academical::HttpClient::ResponseHandler.get_body(env[:response])
22
+ message = ""
23
+
24
+ # If HTML, whole body is taken
25
+ if body.is_a?(String)
26
+ message = body
27
+ end
28
+
29
+ # If JSON, a particular field is taken and used
30
+ if type.include?("json") and body.is_a?(Hash)
31
+ if body.has_key?("message")
32
+ message = body["message"]
33
+ else
34
+ message = "Unable to select error message from json returned by request responsible for error"
35
+ end
36
+ end
37
+
38
+ if message == ""
39
+ message = "Unable to understand the content type of response returned by request responsible for error"
40
+ end
41
+
42
+ raise Academical::Error::ClientError.new message, code
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,32 @@
1
+ module Academical
2
+
3
+ module HttpClient
4
+
5
+ # RequestHandler takes care of encoding the request body into format given by options
6
+ class RequestHandler
7
+
8
+ def self.set_body(options)
9
+ type = options.fetch(:request_type, "json")
10
+
11
+ # Encoding request body into JSON format
12
+ if type == "json"
13
+ request_wrapper = {}
14
+ request_wrapper[:data] = options[:body]
15
+ options[:body] = request_wrapper.to_json
16
+ options[:headers]["content-type"] = "application/json"
17
+ end
18
+
19
+ # Raw body
20
+ if type == "raw"
21
+ options[:body] = options[:body].is_a?(Hash) ? "" : options[:body]
22
+ options[:headers].delete("content-type")
23
+ end
24
+
25
+ return options
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,20 @@
1
+ module Academical
2
+
3
+ module HttpClient
4
+
5
+ # Response object contains the response returned by the client
6
+ class Response
7
+
8
+ attr_accessor :body, :code, :headers
9
+
10
+ def initialize(body, code, headers)
11
+ @body = body
12
+ @code = code
13
+ @headers = headers
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,24 @@
1
+ module Academical
2
+
3
+ module HttpClient
4
+
5
+ # ResponseHandler takes care of decoding the response body into suitable type
6
+ class ResponseHandler
7
+
8
+ def self.get_body(response)
9
+ type = response.headers["content-type"]
10
+ body = response.body
11
+
12
+ # Response body is in JSON
13
+ if type.include?("json")
14
+ body = JSON.parse body
15
+ end
16
+
17
+ return body
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: academical-api-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Academical
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.8
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.7.7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.7.7
41
+ description: Official Academical API library client for ruby
42
+ email: support+developers@academical.co
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/academical-api-client.rb
48
+ - lib/academical/api/schools.rb
49
+ - lib/academical/api/sections.rb
50
+ - lib/academical/api/teachers.rb
51
+ - lib/academical/client.rb
52
+ - lib/academical/error.rb
53
+ - lib/academical/error/client_error.rb
54
+ - lib/academical/http_client.rb
55
+ - lib/academical/http_client/auth_handler.rb
56
+ - lib/academical/http_client/error_handler.rb
57
+ - lib/academical/http_client/request_handler.rb
58
+ - lib/academical/http_client/response.rb
59
+ - lib/academical/http_client/response_handler.rb
60
+ homepage: https://api.academical.co
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.2.2
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Official Academical API library client for ruby
84
+ test_files: []