academical-api-client 0.1.0

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: 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: []