share_learning 0.2.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: 51881b5c17d1723c09e004f02a36627054aef086
4
+ data.tar.gz: ff070d4da0cb833834c221eecdc5ef8f25e5bbd2
5
+ SHA512:
6
+ metadata.gz: 62e1d9b8dbabc97ec0b5b664611c50fd895ba76fca95298c66c19a65897cfe503a43506f2ac3da3cef1245abfe2b33d69151cce57329b777a7e748b555751d39
7
+ data.tar.gz: 472b4fcb6e1f682e8eaa8f7176f0f3ca2a6f3492ab0b4203f3834881889fe1dacc4acd6cf8681a6f2ee7b75c5c38f9e9ec3775e41dcd5dee279e5db2790d7fa9
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ config/
2
+ .DS_Store
3
+ spec/.DS_Store
4
+ lib/.DS_Store
5
+ spec/data/.DS_Store
6
+ coverage/
7
+ spec/fixtures/
8
+ Gemfile.lock
9
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.1
4
+ - 2.2
5
+ - 2.1
6
+ branches:
7
+ only:
8
+ - master
9
+ script: bundle exec rake spec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c)
4
+         ashleycheng <ashley830204@gmail.com>  
5
+ blureze <blureze@gmail.com>
6
+ meegoStar <andy19933@gmail.com>
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+
2
+ # share_learning
3
+ [![Gem Version](https://badge.fury.io/rb/Share_learning.svg)](https://badge.fury.io/rb/Share_learning)
4
+ [![Build Status](https://travis-ci.org/BlueStarAshes/Share_learning.svg?branch=master)](https://travis-ci.org/BlueStarAshes/Share_learning)
5
+
6
+
7
+ ## Introduction
8
+ `share_learning` is aimed to be a crowd sourcing open learning platform.
9
+
10
+ It collects courses links from MOOCS like Coursera, Udacity, Udemy, etc. and relating videos links on Youtube, and lets users easily find learning resources matching their familiarity to a want-to-learn subject / topic on just one website. Users can leave suggestions, tags, and ratings to each learning resource. They can also add relating learning resource links to each subject / topic.
11
+
12
+ But now it is still under construction. :sweat_smile: :beer:
13
+
14
+ ## Installation
15
+ If you are working on a project, add this to your Gemfile: `gem 'share_learning'`
16
+
17
+ For ad hoc installation from command line:
18
+
19
+ `$ gem install share_learning`
20
+
21
+
22
+ ## Setup Youtube Credentials
23
+ Please setup your YouTube credentials by create a project in the Google Developers Console: https://console.developers.google.com and obtain authorization credentials so your application can submit API requests. You should get a YouTube API key.
24
+ ## Usage
25
+
26
+ ### Coursera
27
+ #### Command line
28
+ To use `coursera` in command line, please type as the folling format:
29
+ `coursera [command] [keyword]`
30
+
31
+ `[command]` now includes `[title]`, and `[description]`.
32
+
33
+ * `[title]`
34
+ helps you search courses on Coursea with titles containing the `[keyword]` you give.
35
+
36
+ * `[description]`
37
+ helps you search courses on Coursea with descriptions containing the `[keyword]` you give.
38
+
39
+
40
+ #### In your project
41
+ To use `coursera` in your project, `require 'share_learning'` in your code.
42
+
43
+ See the following code for more detail of the usage.
44
+ ```ruby
45
+ # Access the courses on Coursera
46
+ courses = Coursera::CourseraCourses.find.courses
47
+
48
+ # Check how many courses there are on Coursera
49
+ total = Coursera::CourseraCourses.find.total_course_num
50
+
51
+ # Access each course's title, type, ID, Slug, link, description, and photo URL
52
+ courses.size.times do |i|
53
+ course = courses[i]
54
+ puts "Course #{sequence_number}:\n"\
55
+ "\tTitle: #{course[:course_name]}\n"\
56
+ "\tType: #{course[:course_type]}\n"\
57
+ "\tID: #{course[:course_id]}\n"\
58
+ "\tSlug: #{course[:course_slug]}\n"\
59
+ "\tLink: #{course[:link]}\n"\
60
+ "\tDescription: #{course[:description][0..100]}...\n"\
61
+ "\tPhoto URL: #{course[:photo_url]}\n"\
62
+ "\n"
63
+
64
+ # Search courses with titles or descriptions containing a given keyword
65
+ # results is an array of hash where each hash represents a course
66
+ keyword = 'machine learning'
67
+ results = Coursera::CourseraCourses.find.search_courses(:all, keyword)
68
+
69
+ # Search courses with titles containing a given keyword
70
+ results = Coursera::CourseraCourses.find.search_courses(:course_name, keyword)
71
+
72
+ # Search courses with descriptions containing a given keyword
73
+ results = Coursera::CourseraCourses.find.search_courses(:description, keyword)
74
+ end
75
+ ```
76
+
77
+ ### Udacity
78
+ Udacity application allows you to get the information includes title, introduction, link to the homepage and the image of the course. There are two ways to use the Udacity application:
79
+ #### Command line
80
+
81
+ BASIC USAGE: `udacity [command][feature]`
82
+
83
+ `[command]` now includes `[help]`, `[all]`, `[id]`, `[title]` and `[search]`
84
+ * `[help]`
85
+ Give the introduction of how to use it.
86
+ * `[all]`
87
+ List all the courses on Udacity.
88
+ * `[id]`
89
+ Search a particular course on Udacity with the specific course id.
90
+ * `[title]`
91
+ Search a particular course on Udacity with the specific course title.
92
+ * `[search]`
93
+ Search courses on Udacity with the keyword.
94
+
95
+ `[feature]` is only needed when using the command`[id]`,`[title]` and `[search]`
96
+ * If you're using the command `[id]`, then `[feature]` is the course id
97
+ * If you're using the command `[title]`, then `[feature]` is the course title
98
+ * If you're using the command `[search]`, then `[feature]` is the keyword you want to search
99
+ * Example: `udacity id 'cs101'` or `udacity title 'Introduction to Virtual Reality'` or `udacity search 'java'`
100
+
101
+ #### In your project
102
+
103
+ First, `require 'Share_learning'` in your code.
104
+ See the following example code for more usage details:
105
+
106
+ ```ruby
107
+ # Access courses data
108
+ courses = Udacity::UdacityCourse.find()
109
+
110
+ # Acquire all courses information
111
+ all_courses = courses.acquire_all_courses
112
+
113
+ # Acquire course information with a given course id
114
+ # example: courses.acquire_course_by_id('cs101')
115
+ get_course_by_id = courses.acquire_course_by_id(course_id)
116
+
117
+
118
+ # Acquire course information with a given course title
119
+ # # example: courses.acquire_course_by_title('Introduction to Virtual Reality')
120
+ get_course_by_id = courses.acquire_course_by_title(course_title)
121
+
122
+ ```
123
+
124
+
125
+
126
+ ### YouTube
127
+ #### Command line
128
+ * Setup environment variables: `ENV[YOUTUBE_API_KEY]`
129
+ * USAGE: `youtube [keyword]`
130
+ * `[keyword]` - helps you search and get information of playlists on YouTube.
131
+
132
+ #### In your project
133
+ * `require 'Share_learning'`
134
+ See the following example code for more usage details:
135
+ ```ruby
136
+ # Access playlists data
137
+ playlist_data = YouTube::YouTubePlaylist.find(keyword: 'keyword')
138
+ playlist_data.results.each.with_index do |playlist, index|
139
+ print "#{index + 1}. "
140
+ puts "Playlist on YouTube: #{playlist['title']}"
141
+ puts "Description: #{playlist['description']}"
142
+ puts "Image: #{playlist['image']}"
143
+ puts "URL: #{playlist['url']}"
144
+ puts
145
+ end
146
+ ```
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rake/testtask'
2
+
3
+ namespace :tests do
4
+ desc 'run all tests'
5
+ task all: [:spec_udacity, :spec_coursera, :spec_youtube]
6
+
7
+ task :spec_udacity do
8
+ sh 'ruby spec/udacity_api_spec.rb'
9
+ puts "\n\n"
10
+ end
11
+
12
+ task :spec_coursera do
13
+ sh 'ruby spec/coursera_api_spec.rb'
14
+ puts "\n\n"
15
+ end
16
+
17
+ task :spec_youtube do
18
+ sh 'ruby spec/youtube_api_spec.rb'
19
+ puts "\n\n"
20
+ end
21
+ end
22
+
23
+ desc 'delete cassette fixtures'
24
+ task :wipe do
25
+ sh 'rm spec/fixtures/cassettes/*.yml' do |ok, _|
26
+ puts(ok ? 'Cassettes deleted' : 'No casseettes found')
27
+ end
28
+ end
29
+
30
+ namespace :quality do
31
+ desc 'run all quality checks'
32
+ task all: [:flog, :flay, :rubocop]
33
+
34
+ task :rubocop do
35
+ sh 'rubocop lib/share_learning/udacity_course.rb lib/share_learning/coursera_* lib/share_learning/youtube_playlist.rb'
36
+ puts "\n\n"
37
+ end
38
+
39
+ task :flog do
40
+ sh 'flog lib/share_learning/udacity_course.rb lib/share_learning/coursera_* lib/share_learning/youtube_playlist.rb'
41
+ puts "\n\n"
42
+ end
43
+
44
+ task :flay do
45
+ sh 'flay lib/share_learning/udacity_course.rb lib/share_learning/coursera_* lib/share_learning/youtube_playlist.rb'
46
+ puts "\n\n"
47
+ end
48
+ end
data/bin/coursera ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w(.. lib))
3
+ require 'share_learning'
4
+
5
+ def command_valid?(query)
6
+ %w(title description).include?(query)
7
+ end
8
+
9
+ def help
10
+ puts "USAGE: coursera [command] [keyword]\n\n"\
11
+ "[command] now includes [title], [description].\n"\
12
+ " - \n"\
13
+ " - [title]\t\t"\
14
+ 'helps you search courses on Coursea with titles'\
15
+ " containing the [keyword] you give.\n"\
16
+ " - \n"\
17
+ " - [description]\t"\
18
+ 'helps you search courses on Coursea with descriptions '\
19
+ "containing the [keyword] you give.\n"
20
+ end
21
+
22
+ def reject
23
+ help
24
+ exit(1)
25
+ end
26
+
27
+ def show_course(course, sequence_number)
28
+ puts "Course #{sequence_number}:\n"\
29
+ "\tTitle: #{course[:course_name]}\n"\
30
+ "\tType: #{course[:course_type]}\n"\
31
+ "\tSlug: #{course[:course_slug]}\n"\
32
+ "\tLink: #{course[:link]}\n"\
33
+ "\n"
34
+ end
35
+
36
+ def execute_command(command, keyword)
37
+ case command
38
+ when 'title'
39
+ method = :course_name
40
+ when 'description'
41
+ method = :description
42
+ end
43
+ reject unless keyword
44
+ # search_courses(search_method, keyword)
45
+ results = Coursera::CourseraCourses.find.search_courses(method, keyword)
46
+ results.size.times { |i| show_course(results[i], i + 1) }
47
+ end
48
+
49
+ command = ARGV[0]
50
+ keyword = ARGV[1]
51
+ keyword = keyword.downcase if keyword
52
+
53
+ reject unless command_valid?(command)
54
+
55
+ execute_command(command, keyword)
data/bin/udacity ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w(.. lib))
4
+ require 'share_learning'
5
+
6
+ def show(course)
7
+ puts 'title: ' + course[:title]
8
+ puts 'introduction: ' + course[:intro]
9
+ puts 'link: ' + course[:link]
10
+ end
11
+
12
+ def help
13
+ puts 'USAGE: udacity [command]'
14
+ puts 'command: '
15
+ puts ' - all: show all the courses information in Udacity'
16
+ puts ' - id: search for a course by specific course id'
17
+ puts ' - title: search for a course by specific course title'
18
+ puts ' - search: search for courses related to the keyword'
19
+ end
20
+
21
+ def check_command_available(command)
22
+ case command
23
+ when 'help', 'all', 'id', 'title', 'search'
24
+ else
25
+ puts 'USAGE: udacity [help] [all] [id course_id] [title course_title] [search keyowrd]'
26
+ puts "Use 'udacity help' to see more information."
27
+ exit(1)
28
+ end
29
+ end
30
+
31
+ def check_feature_available(command, feature)
32
+ case command
33
+ when 'id'
34
+ unless feature
35
+ puts 'USAGE: udacity id [course_id]'
36
+ exit(1)
37
+ end
38
+ when 'title'
39
+ unless feature
40
+ puts 'USAGE: udacity title [course_title]'
41
+ exit(1)
42
+ end
43
+ when 'search'
44
+ unless feature
45
+ puts 'USAGE: udacity search [keyword]'
46
+ exit(1)
47
+ end
48
+ end
49
+ end
50
+
51
+ def action(command, feature=nil)
52
+ case command
53
+ when 'help'
54
+ help
55
+ when 'all'
56
+ course = Udacity::UdacityCourse.find().acquire_all_courses
57
+ show(course)
58
+ when 'id'
59
+ check_feature_available('id', feature)
60
+ course = Udacity::UdacityCourse.find().acquire_course_by_id(feature)
61
+ show(course)
62
+ when 'title'
63
+ check_feature_available('title', feature)
64
+ course = Udacity::UdacityCourse.find().acquire_course_by_title(feature)
65
+ show(course)
66
+ when 'search'
67
+ check_feature_available('search', feature)
68
+ course = Udacity::UdacityCourse.find().acquire_courses_by_keywords(feature)
69
+ if course.class == Array
70
+ course.each do |item|
71
+ show(item)
72
+ puts "\n" + "------"
73
+ end
74
+ else
75
+ puts 'no courses found'
76
+ end
77
+ end
78
+ end
79
+
80
+ command = ARGV[0]
81
+ feature = ARGV[1]
82
+ check_command_available(command)
83
+ action(command, feature)
data/bin/youtube ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w(.. lib))
4
+ require 'share_learning'
5
+
6
+ keyword = ARGV[0] || ENV['KEYWORD']
7
+ unless keyword
8
+ puts 'USAGE: share_learning [keyword]'
9
+ exit(1)
10
+ end
11
+
12
+ playlist_data = YouTube::YouTubePlaylist.find(keyword: keyword)
13
+
14
+ playlist_data.results.each.with_index do |playlist, index|
15
+ print "#{index + 1}. "
16
+ puts "Playlist on YouTube: #{playlist['title']}"
17
+ puts "Description: #{playlist['description']}"
18
+ puts "Image: #{playlist['image']}"
19
+ puts "URL: #{playlist['url']}"
20
+ puts
21
+ end
@@ -0,0 +1,110 @@
1
+ require 'http'
2
+ require 'json'
3
+
4
+ module Coursera
5
+ # This class is in charge of the work involving using Coursera API
6
+ class CourseraApi
7
+ COURSERA_CATOLOG_API_URL = 'https://api.coursera.org/api/courses.v1'.freeze
8
+ COURSERA_COURSE_LINK_BASE = 'https://www.coursera.org/learn/'.freeze
9
+ BATCH_SIZE = 100
10
+
11
+ def self.total_course_num
12
+ @total_course_num = retrieve_total_course_num
13
+ end
14
+
15
+ def self.courses
16
+ return @courses if @courses
17
+ @courses = retrieve_all_courses
18
+ end
19
+
20
+ def self.retrieve_total_course_num
21
+ return @total_course_num if @total_course_num
22
+ # Retrieve the total number of courses on the catlog
23
+ course_start_num = 0
24
+ course_num_limit = 0
25
+ query_response =
26
+ HTTP.get(COURSERA_CATOLOG_API_URL,
27
+ params: { start: course_start_num,
28
+ limit: course_num_limit })
29
+ JSON.parse(query_response.body.to_s)['paging']['total']
30
+ end
31
+
32
+ def self.retrieve_all_courses
33
+ # Create an dump for retrieved courses
34
+ retrieved_courses = {}
35
+ # Set the relating numbers
36
+ batch_start = 0
37
+ # Retrieve courses batch by batch
38
+ number_of_batches.times do
39
+ # Jump out the loop if we know there is not any courses left
40
+ break if batch_start >= @total_course_num
41
+
42
+ retrieved_courses = new_batch_merged(retrieved_courses, batch_start)
43
+ # Increase the course number to start with next batch
44
+ batch_start += BATCH_SIZE
45
+ end
46
+ retrieved_courses
47
+ end
48
+
49
+ def self.number_of_batches
50
+ result = retrieve_total_course_num / BATCH_SIZE
51
+ result += 1 unless (@total_course_num % BATCH_SIZE).zero?
52
+ result
53
+ end
54
+
55
+ def self.new_batch_merged(retrieved_courses, course_start)
56
+ # Use the API to get courses response on Coursera
57
+ retrieved_batch =
58
+ query_a_batch_of_courses(course_start)
59
+ # Parse the response to get the desired courses data
60
+ parsed_batch = parse_batch_result(course_start, retrieved_batch)
61
+ # Merge the parsed batch of courses
62
+ retrieved_courses.merge(parsed_batch)
63
+ end
64
+
65
+ def self.query_a_batch_of_courses(course_start)
66
+ # Create an API response dump
67
+ retrieved_batch = {}
68
+ query_response =
69
+ HTTP.get(COURSERA_CATOLOG_API_URL,
70
+ params: { start: course_start,
71
+ limit: BATCH_SIZE,
72
+ fields: 'description,photoUrl' })
73
+ body_str = query_response.body.to_s
74
+ retrieved_batch[:courses] = JSON.parse(body_str)['elements']
75
+ retrieved_batch
76
+ end
77
+
78
+ def self.parse_batch_result(course_start, retrieved_batch)
79
+ # Create an parsed dump
80
+ parsed_courses = {}
81
+ count = course_start
82
+ retrieved_batch[:courses].each do |course|
83
+ parsed_courses[count] = parse_course(course)
84
+ count += 1
85
+ end
86
+ parsed_courses
87
+ end
88
+
89
+ def self.parse_course(course)
90
+ parsed_course = {}
91
+ parsed_course[:course_type] = course['courseType']
92
+ parsed_course[:course_id] = course['id']
93
+ parsed_course[:course_slug] = course['slug']
94
+ parsed_course[:course_name] = course['name']
95
+ parsed_course[:link] =
96
+ COURSERA_COURSE_LINK_BASE + course['slug']
97
+ parsed_course[:description] = course['description']
98
+ parsed_course[:photo_url] = course['photoUrl']
99
+ parsed_course
100
+ end
101
+
102
+ private_class_method :retrieve_total_course_num
103
+ private_class_method :retrieve_all_courses
104
+ private_class_method :number_of_batches
105
+ private_class_method :new_batch_merged
106
+ private_class_method :query_a_batch_of_courses
107
+ private_class_method :parse_batch_result
108
+ private_class_method :parse_course
109
+ end
110
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'coursera_api'
2
+
3
+ module Coursera
4
+ # This class is for Coursera courses
5
+ class CourseraCourses
6
+ attr_reader :total_course_num, :courses
7
+
8
+ def initialize(total_course_num, courses)
9
+ @total_course_num = total_course_num
10
+ @courses = courses
11
+ end
12
+
13
+ def self.find
14
+ total_course_num = CourseraApi.total_course_num
15
+ courses = CourseraApi.courses
16
+ new(total_course_num, courses)
17
+ end
18
+
19
+ def course_matched?(course, method, keyword)
20
+ available_methods = [:course_name, :description]
21
+ result = false
22
+ available_methods.each do |m|
23
+ next unless method == :all || m == method
24
+ result = true if course[m].downcase.include?(keyword)
25
+ end
26
+ result
27
+ end
28
+
29
+ def search_courses(method, keyword)
30
+ matched_courses = []
31
+ @courses.size.times do |i|
32
+ course = courses[i]
33
+ matched_courses.push(course) if course_matched?(course, method, keyword)
34
+ end
35
+ matched_courses
36
+ end
37
+
38
+ def print_all_courses
39
+ puts "#{@total_course_num} courses:"
40
+ @courses.size.times do |i|
41
+ print_course(@courses[i], i)
42
+ end
43
+ end
44
+
45
+ def print_course(course, sequence_number)
46
+ puts "Course #{sequence_number}:\n"\
47
+ "\tTitle: #{course[:course_name]}\n"\
48
+ "\tType: #{course[:course_type]}\n"\
49
+ "\tID: #{course[:course_id]}\n"\
50
+ "\tSlug: #{course[:course_slug]}\n"\
51
+ "\tLink: #{course[:link]}\n"\
52
+ "\tDescription: #{course[:description][0..100]}...\n"\
53
+ "\tPhoto URL: #{course[:photo_url]}\n"\
54
+ "\n"
55
+ end
56
+
57
+ private :print_course
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module ShareLearning
2
+ VERSION = '0.2.0'.freeze
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'http'
2
+ require 'json'
3
+
4
+ module Udacity
5
+ # Service for all Udacity API calls
6
+ class UdacityAPI
7
+ UDACITY_URL = 'https://www.udacity.com/public-api/v0/courses'.freeze
8
+
9
+ # get all courses info in json format through RESTful API
10
+ def self.acquire_json_response
11
+ response = HTTP.get(URI.parse(UDACITY_URL))
12
+ JSON.parse(response)
13
+ end
14
+
15
+ # get total courses number
16
+ def self.total_course_num
17
+ @total_course_num = retrieve_total_course_num
18
+ end
19
+
20
+ def self.retrieve_total_course_num
21
+ return @total_course_num if @total_course_num
22
+
23
+ # Retrieve the total number of courses on the catlog
24
+ json_resp = acquire_json_response
25
+ puts json_resp.class
26
+ @total_course_num = json_resp['courses'].size
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,101 @@
1
+ require 'http'
2
+ require 'json'
3
+ require_relative 'udacity_api'
4
+
5
+ module Udacity
6
+ # Service for all Udacity API calls
7
+ class UdacityCourse
8
+ attr_reader :json_response, :total_course_num
9
+
10
+ def initialize(udacity_api, data, total_course_num)
11
+ @udacity_api = udacity_api
12
+ @json_response = data
13
+ @total_course_num = total_course_num
14
+ end
15
+
16
+ def create_hash(title, intro, link, image)
17
+ { title: title, intro: intro, link: link, image: image }
18
+ end
19
+
20
+ # get all courses information
21
+ def acquire_all_courses
22
+ course_array = []
23
+ @json_response['courses'].each do |course|
24
+ h = create_hash(course['title'], course['summary'], \
25
+ course['homepage'], course['image'])
26
+ course_array.push(h)
27
+ end
28
+ course_array
29
+ end
30
+
31
+ # get course information by course id
32
+ def acquire_course_by_id(id)
33
+ acquire_course_info('key', id)
34
+ end
35
+
36
+ # get course information by course title
37
+ def acquire_course_by_title(title)
38
+ acquire_course_info('title', title)
39
+ end
40
+
41
+ # get course information
42
+ def acquire_course_info(key, value)
43
+ @json_response['courses'].each do |course|
44
+ next unless course[key] == value
45
+ h = create_hash(course['title'], course['summary'], \
46
+ course['homepage'], course['image'])
47
+ return h
48
+ end
49
+ end
50
+
51
+ def substring?(title, summary, keyword)
52
+ ((title.include? keyword) || (summary.include? keyword))
53
+ end
54
+
55
+ def append_course(course_array, course)
56
+ h = create_hash(course['title'], course['summary'], \
57
+ course['homepage'], course['image'])
58
+ course_array.push(h)
59
+ course_array
60
+ end
61
+
62
+ def acquire_courses_by_keywords(keyword)
63
+ course_array = []
64
+ @json_response['courses'].each do |course|
65
+ next unless substring?(course['title'].downcase, \
66
+ course['summary'].downcase, keyword.downcase)
67
+
68
+ course_array = append_course(course_array, course)
69
+ end
70
+
71
+ return 'no courses found' if course_array.empty?
72
+ course_array # return course_array if it is not empty
73
+ end
74
+
75
+ # # get courses by skill levels ('', 'beginner', 'intermediate', 'advanced')
76
+ # def acquire_courses_by_level(level)
77
+ # course_array = []
78
+ # @json_response['courses'].each do |course|
79
+ # next unless course['level'] == level
80
+ # h = create_hash(course['title'], course['summary'], \
81
+ # course['homepage'], course['image'])
82
+ # course_array.push(h)
83
+ # end
84
+ # course_array
85
+ # end
86
+
87
+ # # get courses by tracks (return a list of course id)
88
+ # def acquire_courses_by_tracks(track_name)
89
+ # @json_response['tracks'].each do |track|
90
+ # next unless track['name'] == track_name
91
+ # return track['courses'].inspect
92
+ # end
93
+ # end
94
+
95
+ def self.find
96
+ course_data = UdacityAPI.acquire_json_response
97
+ total_course_num = UdacityAPI.total_course_num
98
+ new(UdacityAPI, course_data, total_course_num)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,26 @@
1
+ require 'http'
2
+ require 'json'
3
+
4
+ module YouTube
5
+ # using YouTube API to get playlists
6
+ class YouTubeAPI
7
+ max_results = 8 # number of videos that should be returned
8
+ YouTube_URL = 'https://www.googleapis.com/youtube/v3/search?part=snippet&type=playlist&order=relevance&maxResults=' + max_results.to_s
9
+
10
+ def self.config=(credentials)
11
+ @config ? @config.update(credentials) : @config = credentials
12
+ end
13
+
14
+ def self.config
15
+ return @config if @config
16
+ @config = { api_key: ENV['YOUTUBE_API_KEY'] } # export YOUTUBE_API_KEY=....
17
+ end
18
+
19
+ # Retrieve the search results
20
+ def self.get_playlist(keyword)
21
+ search_response =
22
+ HTTP.get(YouTube_URL + '&q=' + keyword.split().join('+') + '&key=' + config[:api_key])
23
+ JSON.parse(search_response)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'youtube_api'
2
+
3
+ module YouTube
4
+ # Playlist on Youtube
5
+ class YouTubePlaylist
6
+ attr_reader :results
7
+
8
+ def initialize(data: nil)
9
+ @results = load_data(data)
10
+ end
11
+
12
+ def self.find(keyword:)
13
+ playlists = YouTube::YouTubeAPI.get_playlist(keyword)
14
+ new(data: playlists)
15
+ end
16
+
17
+ private
18
+
19
+ # Get information of playlists
20
+ def load_data(playlists)
21
+ results = []
22
+ playlists['items'].each do |playlist|
23
+ title = playlist['snippet']['title']
24
+ des = playlist['snippet']['description']
25
+ image = playlist['snippet']['thumbnails']['high']['url']
26
+ playlistId = playlist['id']['playlistId']
27
+ url = 'https://www.youtube.com/channel/' + playlistId
28
+ results.push({'title' => title, 'description' => des, 'image' => image, 'url' => url})
29
+ end
30
+ results
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ files = Dir.glob(File.join(File.dirname(__FILE__), 'share_learning/*.rb'))
4
+ files.each { |lib| require_relative lib }
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'share_learning/share_learning_version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'share_learning'
7
+ s.version = ShareLearning::VERSION
8
+
9
+ s.summary = 'Gets learning resource from Coursera, Udacity, and Youtube.'
10
+ s.description = 'Extracts course\'s titles, descriptions, images and links '\
11
+ 'from Coursera and Udacity. '\
12
+ 'And searches relating resource on Youtube.'
13
+ s.authors = ['ashleycheng, blureze, meegoStar']
14
+ s.email = ['ashley830204@gmail.com', 'blureze@gmail.com', 'andy19933@gmail.com']
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- spec/*`.split("\n")
18
+ s.executables << 'udacity' << 'coursera' << 'youtube'
19
+
20
+ s.add_runtime_dependency 'http', '~> 2.0'
21
+
22
+ s.add_development_dependency 'minitest', '~> 5.9'
23
+ s.add_development_dependency 'minitest-rg', '~> 5.2'
24
+ s.add_development_dependency 'rake', '~> 11.2'
25
+ s.add_development_dependency 'vcr', '~> 3.0'
26
+ s.add_development_dependency 'webmock', '~> 2.1'
27
+ s.add_development_dependency 'simplecov', '~> 0.12'
28
+ s.add_development_dependency 'flog', '~> 4.4'
29
+ s.add_development_dependency 'flay', '~> 2.8'
30
+ s.add_development_dependency 'rubocop', '~> 0.44'
31
+ s.homepage = 'https://github.com/BlueStarAshes/share_learning'
32
+ s.license = 'MIT'
33
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'coursera_api_spec_helper'
2
+
3
+ describe 'Coursera API specifications' do
4
+ VCR.configure do |c|
5
+ c.cassette_library_dir = CASSETTES_FOLDER
6
+ c.hook_into :webmock
7
+ end
8
+
9
+ before do
10
+ VCR.insert_cassette CASSETTE_FILE, record: :new_episodes
11
+ end
12
+
13
+ after do
14
+ VCR.eject_cassette
15
+ end
16
+
17
+ it 'should be able to retrieve how many the courses are on the
18
+ Coursera catlog' do
19
+ coursera_courses = Coursera::CourseraCourses.find
20
+ coursera_courses.total_course_num.must_be :>=, 0
21
+ end
22
+
23
+ it 'should be able to retrieve correct number of all courses' do
24
+ coursera_courses = Coursera::CourseraCourses.find
25
+ coursera_courses.courses.size.must_equal coursera_courses.total_course_num
26
+ end
27
+
28
+ it 'should be able to search related courses with the given keyword' do
29
+ keyword = 'machine learning'
30
+
31
+ matched_courses =
32
+ Coursera::CourseraCourses.find.search_courses(:all, keyword)
33
+ matched_courses.count.must_be :>, 0
34
+
35
+ matched_courses_by_title =
36
+ Coursera::CourseraCourses.find.search_courses(:course_name, keyword)
37
+ matched_courses_by_title.count.must_be :>, 0
38
+
39
+ matched_courses_by_description =
40
+ Coursera::CourseraCourses.find.search_courses(:description, keyword)
41
+ matched_courses_by_description.count.must_be :>, 0
42
+
43
+ matched_courses.count.must_be :>=, matched_courses_by_title.count
44
+ matched_courses.count.must_be :>=, matched_courses_by_description.count
45
+ sum = matched_courses_by_title.count + matched_courses_by_description.count
46
+ matched_courses.count.must_be :<=, sum
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/rg'
6
+ require 'vcr'
7
+ require 'webmock'
8
+
9
+ require_relative '../lib/share_learning/coursera_courses.rb'
10
+
11
+ FIXTURE_FOLDER = 'spec/fixtures'.freeze
12
+ CASSETTES_FOLDER = "#{FIXTURE_FOLDER}/cassettes".freeze
13
+ CASSETTE_FILE = 'coursera_api'.freeze
@@ -0,0 +1,17 @@
1
+ {
2
+ "get_course_by_id":
3
+ {
4
+ "title": "Intro to Computer Science",
5
+ "intro": "In this introduction to computer programming course, you’ll learn and practice key computer science concepts by building your own versions of popular web applications. You’ll learn Python, a powerful, easy-to-learn, and widely used programming language, and you’ll explore computer science basics, as you build your own search engine and social network.",
6
+ "link": "https://www.udacity.com/course/intro-to-computer-science--cs101?utm_medium=referral&utm_campaign=api",
7
+ "image": "https://lh5.ggpht.com/ITepKi-2pz4Q6lrLfv6QDNViEGIfxyupzgQwx1YgS4L8m3MFITBKWDpaZb_VoAP-zV3bEEoIbFY7mauj8HM=s0#w=1724&h=1060"
8
+ }
9
+ ,
10
+ "get_course_by_title":
11
+ {
12
+ "title": "Intro to Java Programming",
13
+ "intro": "In this introductory course, you'll learn and practice essential computer science concepts using the Java programming language. You'll learn about Object Oriented Programming, a technique that allows you to use code written by other programmers in your own programs. You'll put your new Java programming skills to the test by solving real-world problems faced by software engineers.",
14
+ "link": "https://www.udacity.com/course/intro-to-java-programming--cs046?utm_medium=referral&utm_campaign=api",
15
+ "image": "https://lh4.ggpht.com/9ytiUdz0QYHwuMJFTXcNXZn4FctGW6Zszm7Aj5s7mXHHXsapIKYPL08vPWeghAjF2QmuhPiYCU2Q3kNeW7w=s0#w=1725&h=1060"
16
+ }
17
+ }
@@ -0,0 +1,64 @@
1
+ require_relative 'udacity_api_spec_helper.rb'
2
+
3
+ describe 'Udacity course' do
4
+ VCR.configure do |c|
5
+ c.cassette_library_dir = CASSETTES_FOLDER
6
+ c.hook_into :webmock
7
+ end
8
+
9
+ before do
10
+ VCR.insert_cassette CASSETTE_FILE, record: :new_episodes
11
+
12
+ @udacity_api = Udacity::UdacityAPI
13
+ end
14
+
15
+ after do
16
+ VCR.eject_cassette
17
+ end
18
+
19
+ it 'should get json response successfully' do
20
+ udacity = Udacity::UdacityAPI.acquire_json_response
21
+ udacity.nil?.must_equal false
22
+ end
23
+
24
+ it 'should get all courses successfully' do
25
+ udacity = Udacity::UdacityCourse.find()
26
+ all_courses = udacity.acquire_all_courses
27
+ all_courses.nil?.must_equal false
28
+ end
29
+
30
+ it 'should get course by id successfully' do
31
+ udacity = Udacity::UdacityCourse.find()
32
+ course = udacity.acquire_course_by_id('cs101')
33
+ course.must_equal (UDACITY_RESULT[:get_course_by_id])
34
+ end
35
+
36
+ it 'should get course by title successfully' do
37
+ udacity = Udacity::UdacityCourse.find()
38
+ course = udacity.acquire_course_by_title('Intro to Java Programming')
39
+ course.must_equal (UDACITY_RESULT[:get_course_by_title])
40
+ end
41
+
42
+ it 'should get course by keywords successfully' do
43
+ udacity = Udacity::UdacityCourse.find()
44
+ course = udacity.acquire_courses_by_keywords('Java Programming')
45
+ course.nil?.must_equal false
46
+ end
47
+
48
+ it 'should get the number of course successfully' do
49
+ udacity = Udacity::UdacityCourse.find()
50
+ udacity.total_course_num.must_be :>=, 0
51
+ end
52
+
53
+ # it 'should get courses by level successfully' do
54
+ # udacity = Udacity::UdacityCourse.find()
55
+ # course = udacity.acquire_courses_by_level('beginner')
56
+ # course.nil?.must_equal false
57
+ # end
58
+
59
+ # it 'should get courses by tracks successfully' do
60
+ # udacity = Udacity::UdacityCourse.find()
61
+ # course = udacity.acquire_courses_by_tracks('Data Science')
62
+ # course.nil?.must_equal false
63
+ # end
64
+ end
@@ -0,0 +1,18 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/rg'
6
+ require 'json'
7
+ require 'yaml'
8
+ require 'vcr'
9
+ require 'webmock'
10
+
11
+ require_relative '../lib/share_learning/udacity_api.rb'
12
+ require_relative '../lib/share_learning/udacity_course.rb'
13
+
14
+ FIXTURES_FOLDER = 'spec/fixtures'
15
+ CASSETTES_FOLDER = "#{FIXTURES_FOLDER}/cassettes"
16
+ CASSETTE_FILE = 'udacity_api'
17
+ RESULT_FILE = "#{FIXTURES_FOLDER}/udacity_api_results.json"
18
+ UDACITY_RESULT = JSON.parse(File.read(RESULT_FILE), :symbolize_names => true)
@@ -0,0 +1,51 @@
1
+ require_relative 'udacity_exe_spec_helper.rb'
2
+
3
+ describe 'Udacity executable files' do
4
+ VCR.configure do |c|
5
+ c.cassette_library_dir = CASSETTES_FOLDER
6
+ c.hook_into :webmock
7
+ end
8
+
9
+ before do
10
+ VCR.insert_cassette CASSETTE_FILE, record: :new_episodes
11
+ end
12
+
13
+ after do
14
+ VCR.eject_cassette
15
+ end
16
+
17
+ it 'should support help instruction' do
18
+ result = help
19
+ result.must_equal (UDACITY_RESULT['help'])
20
+ end
21
+
22
+ it 'should get all courses successfully' do
23
+ result = action('all')
24
+ result.nil?.must_equal false
25
+ end
26
+
27
+ it 'should get course by id successfully' do
28
+ result = action('id', 'cs101')
29
+ result.must_equal (UDACITY_RESULT['get_course_by_id'])
30
+ end
31
+
32
+ it 'should get course by title successfully' do
33
+ result = action('title', 'Intro to Java Programming')
34
+ result.must_equal (UDACITY_RESULT['get_course_by_title'])
35
+ end
36
+
37
+ it 'should throw error message when given wrong command' do
38
+ result = check_command_available('ttt')
39
+ result.must_equal (UDACITY_RESULT['error_command'])
40
+ end
41
+
42
+ it 'should throw error message when given wrong feature for id' do
43
+ result = check_feature_available('ttt')
44
+ result.must_equal (UDACITY_RESULT['error_feature']['id'])
45
+ end
46
+
47
+ it 'should throw error message when given wrong feature for title' do
48
+ result = check_feature_available('ttt')
49
+ result.must_equal (UDACITY_RESULT['error_feature']['title'])
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/rg'
6
+ require 'yaml'
7
+ require 'vcr'
8
+ require 'webmock'
9
+
10
+ require 'udacity'
11
+
12
+ FIXTURES_FOLDER = 'spec/fixtures'.freeze
13
+ CASSETTES_FOLDER = "#{FIXTURES_FOLDER}/cassettes".freeze
14
+ CASSETTE_FILE = 'udacity_exe'.freeze
15
+ RESULT_FILE = "#{FIXTURES_FOLDER}/udacity_exe_results.yml".freeze
16
+ UDACITY_RESULT = YAML.load(File.read(RESULT_FILE))
@@ -0,0 +1,23 @@
1
+ require_relative 'youtube_api_spec_helper.rb'
2
+
3
+ describe 'YouTube API' do
4
+ VCR.configure do |c|
5
+ c.cassette_library_dir = CASSETTES_FOLDER
6
+ c.hook_into :webmock
7
+
8
+ c.filter_sensitive_data('<API_KEY>') { ENV['YOUTUBE_API_KEY'] }
9
+ end
10
+
11
+ before do
12
+ VCR.insert_cassette CASSETTE_FILE, record: :new_episodes
13
+ end
14
+
15
+ after do
16
+ VCR.eject_cassette
17
+ end
18
+
19
+ it 'should get playlist by keyword' do
20
+ courses = YouTube::YouTubePlaylist.find(keyword: 'machine learning')
21
+ courses.results.count.must_be :>=, 0
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'minitest/autorun'
5
+ require 'minitest/rg'
6
+ require 'yaml'
7
+ require 'json'
8
+ require 'vcr'
9
+ require 'webmock'
10
+
11
+ require_relative '../lib/share_learning'
12
+
13
+ FIXTURES_FOLDER = 'fixtures'.freeze
14
+ CASSETTES_FOLDER = "#{FIXTURES_FOLDER}/cassettes".freeze
15
+ CASSETTE_FILE = 'youtube_api'.freeze
16
+
17
+ # read credentials from a Yaml file into environment variables
18
+ if File.file?('../config/credentials.yml')
19
+ credentials = YAML.load(File.read('../config/credentials.yml'))
20
+ ENV['YOUTUBE_API_KEY'] = credentials[:api_key]
21
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: share_learning
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - ashleycheng, blureze, meegoStar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest-rg
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '11.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '11.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: vcr
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.12'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.12'
111
+ - !ruby/object:Gem::Dependency
112
+ name: flog
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.4'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.4'
125
+ - !ruby/object:Gem::Dependency
126
+ name: flay
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.8'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.8'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.44'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.44'
153
+ description: Extracts course's titles, descriptions, images and links from Coursera
154
+ and Udacity. And searches relating resource on Youtube.
155
+ email:
156
+ - ashley830204@gmail.com
157
+ - blureze@gmail.com
158
+ - andy19933@gmail.com
159
+ executables:
160
+ - udacity
161
+ - coursera
162
+ - youtube
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - ".gitignore"
167
+ - ".travis.yml"
168
+ - Gemfile
169
+ - LICENSE
170
+ - README.md
171
+ - Rakefile
172
+ - bin/coursera
173
+ - bin/udacity
174
+ - bin/youtube
175
+ - lib/share_learning.rb
176
+ - lib/share_learning/coursera_api.rb
177
+ - lib/share_learning/coursera_courses.rb
178
+ - lib/share_learning/share_learning_version.rb
179
+ - lib/share_learning/udacity_api.rb
180
+ - lib/share_learning/udacity_course.rb
181
+ - lib/share_learning/youtube_api.rb
182
+ - lib/share_learning/youtube_playlist.rb
183
+ - share_learning.gemspec
184
+ - spec/coursera_api_spec.rb
185
+ - spec/coursera_api_spec_helper.rb
186
+ - spec/data/course_test_data.json
187
+ - spec/udacity_api_spec.rb
188
+ - spec/udacity_api_spec_helper.rb
189
+ - spec/udacity_exe_spec.rb
190
+ - spec/udacity_exe_spec_helper.rb
191
+ - spec/youtube_api_spec.rb
192
+ - spec/youtube_api_spec_helper.rb
193
+ homepage: https://github.com/BlueStarAshes/share_learning
194
+ licenses:
195
+ - MIT
196
+ metadata: {}
197
+ post_install_message:
198
+ rdoc_options: []
199
+ require_paths:
200
+ - lib
201
+ required_ruby_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ required_rubygems_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ requirements: []
212
+ rubyforge_project:
213
+ rubygems_version: 2.6.8
214
+ signing_key:
215
+ specification_version: 4
216
+ summary: Gets learning resource from Coursera, Udacity, and Youtube.
217
+ test_files:
218
+ - spec/coursera_api_spec.rb
219
+ - spec/coursera_api_spec_helper.rb
220
+ - spec/data/course_test_data.json
221
+ - spec/udacity_api_spec.rb
222
+ - spec/udacity_api_spec_helper.rb
223
+ - spec/udacity_exe_spec.rb
224
+ - spec/udacity_exe_spec_helper.rb
225
+ - spec/youtube_api_spec.rb
226
+ - spec/youtube_api_spec_helper.rb