facegroups 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 +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +89 -0
- data/LICENSE +21 -0
- data/README.md +55 -0
- data/Rakefile +54 -0
- data/bin/facegroups +21 -0
- data/config/credentials.yml.example +5 -0
- data/facegroups.gemspec +32 -0
- data/lib/facegroups/attachment.rb +16 -0
- data/lib/facegroups/fb_api.rb +73 -0
- data/lib/facegroups/feed.rb +23 -0
- data/lib/facegroups/group.rb +21 -0
- data/lib/facegroups/posting.rb +31 -0
- data/lib/facegroups/version.rb +5 -0
- data/lib/facegroups.rb +4 -0
- data/spec/facegroups_spec.rb +83 -0
- data/spec/fixtures/cassettes/facebook_api.yml +297 -0
- data/spec/fixtures/fb_api_results.yml +22 -0
- data/spec/spec_helper.rb +26 -0
- metadata +211 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 92303bfc890617bfc360e4b089049596605649cb
|
4
|
+
data.tar.gz: f8bce6b8de9f77dc300239e56d1628adc2f04521
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1088ff453e77ea1e3e444ae4bf545228ea6ed31bd4f0d98ed813d72aa2f651c3cf6e6beb0c30e3e727cfa905a94dad7e8dd5f10add803136f25c04743f48da82
|
7
|
+
data.tar.gz: a9f26a451f7761f5584721b769d5224730520e309d364da5a1d246a5b99ab3d1c6eccfc4719359c3a51c15306f2a8da488fffa03b61ed581ef612cf56c614668
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
facegroups (0.1.0)
|
5
|
+
http (~> 2.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
addressable (2.4.0)
|
11
|
+
ast (2.3.0)
|
12
|
+
crack (0.4.3)
|
13
|
+
safe_yaml (~> 1.0.0)
|
14
|
+
docile (1.1.5)
|
15
|
+
domain_name (0.5.20160826)
|
16
|
+
unf (>= 0.0.5, < 1.0.0)
|
17
|
+
erubis (2.7.0)
|
18
|
+
flay (2.8.1)
|
19
|
+
erubis (~> 2.7.0)
|
20
|
+
path_expander (~> 1.0)
|
21
|
+
ruby_parser (~> 3.0)
|
22
|
+
sexp_processor (~> 4.0)
|
23
|
+
flog (4.4.0)
|
24
|
+
path_expander (~> 1.0)
|
25
|
+
ruby_parser (~> 3.1, > 3.1.0)
|
26
|
+
sexp_processor (~> 4.4)
|
27
|
+
hashdiff (0.3.0)
|
28
|
+
http (2.0.3)
|
29
|
+
addressable (~> 2.3)
|
30
|
+
http-cookie (~> 1.0)
|
31
|
+
http-form_data (~> 1.0.1)
|
32
|
+
http_parser.rb (~> 0.6.0)
|
33
|
+
http-cookie (1.0.3)
|
34
|
+
domain_name (~> 0.5)
|
35
|
+
http-form_data (1.0.1)
|
36
|
+
http_parser.rb (0.6.0)
|
37
|
+
json (2.0.2)
|
38
|
+
minitest (5.9.1)
|
39
|
+
minitest-rg (5.2.0)
|
40
|
+
minitest (~> 5.0)
|
41
|
+
parser (2.3.1.4)
|
42
|
+
ast (~> 2.2)
|
43
|
+
path_expander (1.0.0)
|
44
|
+
powerpack (0.1.1)
|
45
|
+
rainbow (2.1.0)
|
46
|
+
rake (11.3.0)
|
47
|
+
rubocop (0.44.1)
|
48
|
+
parser (>= 2.3.1.1, < 3.0)
|
49
|
+
powerpack (~> 0.1)
|
50
|
+
rainbow (>= 1.99.1, < 3.0)
|
51
|
+
ruby-progressbar (~> 1.7)
|
52
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
53
|
+
ruby-progressbar (1.8.1)
|
54
|
+
ruby_parser (3.8.3)
|
55
|
+
sexp_processor (~> 4.1)
|
56
|
+
safe_yaml (1.0.4)
|
57
|
+
sexp_processor (4.7.0)
|
58
|
+
simplecov (0.12.0)
|
59
|
+
docile (~> 1.1.0)
|
60
|
+
json (>= 1.8, < 3)
|
61
|
+
simplecov-html (~> 0.10.0)
|
62
|
+
simplecov-html (0.10.0)
|
63
|
+
unf (0.1.4)
|
64
|
+
unf_ext
|
65
|
+
unf_ext (0.0.7.2)
|
66
|
+
unicode-display_width (1.1.1)
|
67
|
+
vcr (3.0.3)
|
68
|
+
webmock (2.1.0)
|
69
|
+
addressable (>= 2.3.6)
|
70
|
+
crack (>= 0.3.2)
|
71
|
+
hashdiff
|
72
|
+
|
73
|
+
PLATFORMS
|
74
|
+
ruby
|
75
|
+
|
76
|
+
DEPENDENCIES
|
77
|
+
facegroups!
|
78
|
+
flay (~> 2.8)
|
79
|
+
flog (~> 4.4)
|
80
|
+
minitest (~> 5.9)
|
81
|
+
minitest-rg (~> 5.2)
|
82
|
+
rake (~> 11.3)
|
83
|
+
rubocop (~> 0.42)
|
84
|
+
simplecov (~> 0.12)
|
85
|
+
vcr (~> 3.0)
|
86
|
+
webmock (~> 2.1)
|
87
|
+
|
88
|
+
BUNDLED WITH
|
89
|
+
1.13.6
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Aditya Utama Wijaya
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# FaceGroups Gem
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/facegroup)
|
4
|
+
[](https://travis-ci.org/aditya-utama-wijaya/facegroup)
|
5
|
+
|
6
|
+
FaceGroups is a gem that specializes in getting data from public Facebook Groups.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
If you are working on a project, add this to your Gemfile: `gem 'facegroups'`
|
11
|
+
|
12
|
+
For ad hoc installation from command line:
|
13
|
+
```$ gem install facegroups```
|
14
|
+
|
15
|
+
## Setup Facebook Credentials
|
16
|
+
|
17
|
+
Please setup your Facebook developer credentials by creating an app profile on Facebook Developer: https://developers.facebook.com - you should get a "client ID" and "client secret".
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Require FaceGroups gem in your code: `require 'facegroups'`
|
22
|
+
|
23
|
+
Supply your Facebook credentials to our library in one of two ways:
|
24
|
+
- Setup environment variables: `ENV[FB_CLIENT_ID]` and `ENV[FB_CLIENT_SECRET]`
|
25
|
+
- or, provide them directly to FaceGroups:
|
26
|
+
|
27
|
+
```
|
28
|
+
FaceGroups::FbApi.config = {
|
29
|
+
client_id: ENV[FB_CLIENT_ID],
|
30
|
+
client_secret: ENV[FB_CLIENT_SECRET]
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
See the following example code for more usage details:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Access the group
|
38
|
+
group = FaceGroups::Group.find(id: ENV['FB_GROUP_ID'])
|
39
|
+
|
40
|
+
puts group.name
|
41
|
+
|
42
|
+
feed = group.feed
|
43
|
+
|
44
|
+
puts feed.count
|
45
|
+
|
46
|
+
group.feed.postings.each do |posting|
|
47
|
+
puts posting.id
|
48
|
+
puts posting.created_time
|
49
|
+
puts posting.message
|
50
|
+
if posting.attachment
|
51
|
+
puts posting.attachment.description
|
52
|
+
puts posting.attachment.url
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
task default: :spec
|
5
|
+
|
6
|
+
namespace :credentials do
|
7
|
+
require 'yaml'
|
8
|
+
desc 'generate access_token to STDOUT'
|
9
|
+
task :get_access_token do
|
10
|
+
credentials = YAML.load(File.read('config/credentials.yml'))
|
11
|
+
require_relative 'lib/fb_api'
|
12
|
+
ENV['FBAPI_CLIENT_ID'] = credentials[:client_id]
|
13
|
+
ENV['FBAPI_CLIENT_SECRET'] = credentials[:client_secret]
|
14
|
+
|
15
|
+
puts "Access Token: #{FaceGroups::FbApi.access_token}"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Export sample credentials from file to bash'
|
19
|
+
task :export do
|
20
|
+
credentials = YAML.load(File.read('config/credentials.yml'))
|
21
|
+
puts 'Please run the following in bash:'
|
22
|
+
puts "export FBAPI_CLIENT_ID=#{credentials[:client_id]}"
|
23
|
+
puts "export FBAPI_CLIENT_SECRET=#{credentials[:client_secret]}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'run tests'
|
28
|
+
task :spec do
|
29
|
+
sh 'ruby spec/facegroups_spec.rb'
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'delete cassette fixtures'
|
33
|
+
task :wipe do
|
34
|
+
sh 'rm spec/fixtures/cassettes/*.yml' do |ok, _|
|
35
|
+
puts(ok ? 'Cassettes deleted' : 'No cassettes found')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
namespace :quality do
|
40
|
+
desc 'run all quality checks'
|
41
|
+
task all: [:rubocop, :flog, :flay]
|
42
|
+
|
43
|
+
task :flog do
|
44
|
+
sh 'flog lib/'
|
45
|
+
end
|
46
|
+
|
47
|
+
task :flay do
|
48
|
+
sh 'flay lib/'
|
49
|
+
end
|
50
|
+
|
51
|
+
task :rubocop do
|
52
|
+
sh 'rubocop'
|
53
|
+
end
|
54
|
+
end
|
data/bin/facegroups
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 'facegroups'
|
5
|
+
|
6
|
+
group_id = ARGV[0] || ENV['FB_GROUP_ID']
|
7
|
+
unless group_id
|
8
|
+
puts 'USAGE: facegroups [group_id]'
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
group = FaceGroups::Group.find(id: group_id)
|
13
|
+
|
14
|
+
puts group.name
|
15
|
+
puts Array.new(group.name.length) { '-' }.join
|
16
|
+
group.feed.postings.first(3).each.with_index do |post, index|
|
17
|
+
print "#{index + 1}"
|
18
|
+
puts post.message ? post.message : '(blank)'
|
19
|
+
puts "Attached: #{post.attachment.url}" if post.attachment
|
20
|
+
puts
|
21
|
+
end
|
data/facegroups.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'facegroups/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'facegroups'
|
7
|
+
s.version = FaceGroups::VERSION
|
8
|
+
|
9
|
+
s.summary = 'Gets posted content from public Facebook groups'
|
10
|
+
s.description = 'Extracts feed, postings, and attachments from FB groups'
|
11
|
+
s.authors = ['Aditya Utama Wijaya']
|
12
|
+
s.email = ['adityautamawijaya@gmail.com']
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
16
|
+
s.executables << 'facegroups'
|
17
|
+
|
18
|
+
s.add_runtime_dependency 'http', '~> 2.0'
|
19
|
+
|
20
|
+
s.add_development_dependency 'minitest', '~> 5.9'
|
21
|
+
s.add_development_dependency 'minitest-rg', '~> 5.2'
|
22
|
+
s.add_development_dependency 'rake', '~> 11.3'
|
23
|
+
s.add_development_dependency 'vcr', '~> 3.0'
|
24
|
+
s.add_development_dependency 'webmock', '~> 2.1'
|
25
|
+
s.add_development_dependency 'simplecov', '~> 0.12'
|
26
|
+
s.add_development_dependency 'flog', '~> 4.4'
|
27
|
+
s.add_development_dependency 'flay', '~> 2.8'
|
28
|
+
s.add_development_dependency 'rubocop', '~> 0.42'
|
29
|
+
|
30
|
+
s.homepage = 'https://github.com/aditya-utama-wijaya/facegroups'
|
31
|
+
s.license = 'MIT'
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FaceGroups
|
4
|
+
# Attached URL to Posting
|
5
|
+
class Attachment
|
6
|
+
attr_reader :title, :description, :url
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
return unless data
|
10
|
+
attachment_data = data['data'].first
|
11
|
+
@title = attachment_data['title']
|
12
|
+
@description = attachment_data['description']
|
13
|
+
@url = attachment_data['url']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'http'
|
3
|
+
|
4
|
+
module FaceGroups
|
5
|
+
# Service for all FB API calls
|
6
|
+
class FbApi
|
7
|
+
FB_URL = 'https://graph.facebook.com'
|
8
|
+
API_VER = 'v2.8'
|
9
|
+
FB_API_URL = [FB_URL, API_VER].join('/')
|
10
|
+
FB_TOKEN_URL = [FB_API_URL, 'oauth/access_token'].join('/')
|
11
|
+
TOKEN_KEY = 'fbapi_access_token'
|
12
|
+
|
13
|
+
GRAPH_QUERY = {
|
14
|
+
group:
|
15
|
+
'id,name,feed{name,message,updated_time,created_time,'\
|
16
|
+
'attachments{title,description,url,media}}',
|
17
|
+
posting:
|
18
|
+
'name,message,updated_time,created_time,'\
|
19
|
+
'attachments{title,description,url,media}'
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
def self.access_token
|
23
|
+
return @access_token if @access_token
|
24
|
+
|
25
|
+
access_token_response = HTTP.get(
|
26
|
+
FB_TOKEN_URL,
|
27
|
+
params: {
|
28
|
+
client_id: config[:client_id],
|
29
|
+
client_secret: config[:client_secret],
|
30
|
+
grant_type: 'client_credentials'
|
31
|
+
}
|
32
|
+
)
|
33
|
+
@access_token = access_token_response.parse['access_token']
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.config=(credentials)
|
37
|
+
@config ? @config.update(credentials) : @config = credentials
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.config
|
41
|
+
return @config if @config
|
42
|
+
@config = {
|
43
|
+
client_id: ENV['FB_CLIENT_ID'],
|
44
|
+
client_secret: ENV['FB_CLIENT_SECRET']
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.group_data(group_id)
|
49
|
+
graphql_query(group_id, :group)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.posting_data(posting_id)
|
53
|
+
graphql_query(posting_id, :posting)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.graphql_query(resource_id, resource_key)
|
57
|
+
response = HTTP.get(
|
58
|
+
fb_resource_url(resource_id),
|
59
|
+
params: {
|
60
|
+
fields: GRAPH_QUERY[resource_key],
|
61
|
+
access_token: access_token
|
62
|
+
}
|
63
|
+
)
|
64
|
+
JSON.load(response.to_s)
|
65
|
+
end
|
66
|
+
|
67
|
+
private_class_method
|
68
|
+
|
69
|
+
def self.fb_resource_url(id)
|
70
|
+
URI.join(FB_API_URL, id.to_s).to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'fb_api'
|
3
|
+
require_relative 'posting'
|
4
|
+
|
5
|
+
module FaceGroups
|
6
|
+
# Group feeds, with data and paging information
|
7
|
+
class Feed
|
8
|
+
attr_reader :postings
|
9
|
+
|
10
|
+
def initialize(feed_data:)
|
11
|
+
postings_data = feed_data['data']
|
12
|
+
@postings = postings_data.map do |post_data|
|
13
|
+
FaceGroups::Posting.new(data: post_data)
|
14
|
+
end
|
15
|
+
|
16
|
+
@pagination = feed_data['pagination']
|
17
|
+
end
|
18
|
+
|
19
|
+
def count
|
20
|
+
@postings.count
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'fb_api'
|
3
|
+
require_relative 'feed'
|
4
|
+
|
5
|
+
module FaceGroups
|
6
|
+
# Main class to setup a Facebook group
|
7
|
+
class Group
|
8
|
+
attr_reader :id, :name, :feed
|
9
|
+
|
10
|
+
def initialize(group_data:)
|
11
|
+
@name = group_data['name']
|
12
|
+
@id = group_data['id']
|
13
|
+
@feed = Feed.new(feed_data: group_data['feed'])
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.find(id:)
|
17
|
+
group_data = FbApi.group_data(id)
|
18
|
+
new(group_data: group_data)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'fb_api'
|
3
|
+
require_relative 'attachment'
|
4
|
+
|
5
|
+
module FaceGroups
|
6
|
+
# Single posting on group's feed
|
7
|
+
class Posting
|
8
|
+
attr_reader :id, :created_time, :updated_time, :message, :name, :attachment
|
9
|
+
|
10
|
+
def initialize(data: nil)
|
11
|
+
load_data(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.find(id:)
|
15
|
+
posting_data = FaceGroups::FbApi.posting_data(id)
|
16
|
+
new(data: posting_data)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_data(posting_data)
|
22
|
+
@id = posting_data['id']
|
23
|
+
@updated_time = posting_data['updated_time']
|
24
|
+
@created_time = posting_data['created_time']
|
25
|
+
@name = posting_data['message']
|
26
|
+
@message = posting_data['message']
|
27
|
+
attached = posting_data['attachments']
|
28
|
+
@attachment = Attachment.new(attached) if attached
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/facegroups.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'spec_helper.rb'
|
3
|
+
|
4
|
+
describe 'FaceGroups specifications' do
|
5
|
+
VCR.configure do |c|
|
6
|
+
c.cassette_library_dir = CASSETTES_FOLDER
|
7
|
+
c.hook_into :webmock
|
8
|
+
|
9
|
+
c.filter_sensitive_data('<ACCESS_TOKEN>') do
|
10
|
+
URI.unescape(ENV['FB_ACCESS_TOKEN'])
|
11
|
+
end
|
12
|
+
|
13
|
+
c.filter_sensitive_data('<ACCESS_TOKEN_ESCAPED>') do
|
14
|
+
ENV['FB_ACCESS_TOKEN']
|
15
|
+
end
|
16
|
+
|
17
|
+
c.filter_sensitive_data('<CLIENT_ID>') { ENV['FB_CLIENT_ID'] }
|
18
|
+
c.filter_sensitive_data('<CLIENT_SECRET>') { ENV['FB_CLIENT_SECRET'] }
|
19
|
+
end
|
20
|
+
|
21
|
+
before do
|
22
|
+
VCR.insert_cassette CASSETTE_FILE, record: :new_episodes
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
VCR.eject_cassette
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'FbApi Credentials' do
|
30
|
+
it 'should be able to get a new access token with ENV credentials' do
|
31
|
+
FaceGroups::FbApi.access_token.length.must_be :>, 0
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should be able to get a new access token with file credentials' do
|
35
|
+
FaceGroups::FbApi.config = {
|
36
|
+
client_id: ENV['FB_CLIENT_ID'],
|
37
|
+
client_secret: ENV['FB_CLIENT_SECRET']
|
38
|
+
}
|
39
|
+
FaceGroups::FbApi.access_token.length.must_be :>, 0
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should be able to open a Facebook Group' do
|
44
|
+
group = FaceGroups::Group.find(
|
45
|
+
id: ENV['FB_GROUP_ID']
|
46
|
+
)
|
47
|
+
|
48
|
+
group.name.length.must_be :>, 0
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should get the latest feed from a group' do
|
52
|
+
group = FaceGroups::Group.find(
|
53
|
+
id: ENV['FB_GROUP_ID']
|
54
|
+
)
|
55
|
+
|
56
|
+
feed = group.feed
|
57
|
+
feed.count.must_be :>, 1
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should get basic information about postings on the feed' do
|
61
|
+
group = FaceGroups::Group.find(
|
62
|
+
id: ENV['FB_GROUP_ID']
|
63
|
+
)
|
64
|
+
|
65
|
+
group.feed.postings.each do |posting|
|
66
|
+
posting.id.wont_be_nil
|
67
|
+
posting.updated_time.wont_be_nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should find all parts of a full posting' do
|
72
|
+
posting = FB_RESULT['posting']
|
73
|
+
attachment = posting['attachment'].first
|
74
|
+
retrieved = FaceGroups::Posting.find(id: posting['id'])
|
75
|
+
|
76
|
+
retrieved.id.must_equal posting['id']
|
77
|
+
retrieved.created_time.must_equal posting['created_time']
|
78
|
+
retrieved.message.must_equal posting['message']
|
79
|
+
retrieved.attachment.wont_be_nil
|
80
|
+
retrieved.attachment.description.must_equal attachment['description']
|
81
|
+
retrieved.attachment.url.must_match 'tutorialzine'
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,297 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://graph.facebook.com/v2.8/oauth/access_token?client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&grant_type=client_credentials
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- graph.facebook.com
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/2.0.3
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Access-Control-Allow-Origin:
|
22
|
+
- "*"
|
23
|
+
Pragma:
|
24
|
+
- no-cache
|
25
|
+
Cache-Control:
|
26
|
+
- private, no-cache, no-store, must-revalidate
|
27
|
+
Facebook-Api-Version:
|
28
|
+
- v2.8
|
29
|
+
Expires:
|
30
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
31
|
+
Content-Type:
|
32
|
+
- application/json; charset=UTF-8
|
33
|
+
X-Fb-Trace-Id:
|
34
|
+
- C+G0/33ZH0o
|
35
|
+
X-Fb-Rev:
|
36
|
+
- '2654373'
|
37
|
+
X-Fb-Debug:
|
38
|
+
- 6W+8KMRw3wPdtxDUy9k0/LourAqASLcTLLXbyC4OzbE2+xlYzurPkMFOOEROugODHwdIsC0Bu4WDL6o1eIs3Wg==
|
39
|
+
Date:
|
40
|
+
- Mon, 31 Oct 2016 11:36:31 GMT
|
41
|
+
Connection:
|
42
|
+
- close
|
43
|
+
Content-Length:
|
44
|
+
- '85'
|
45
|
+
body:
|
46
|
+
encoding: UTF-8
|
47
|
+
string: '{"access_token":"<ACCESS_TOKEN>","token_type":"bearer"}'
|
48
|
+
http_version:
|
49
|
+
recorded_at: Mon, 31 Oct 2016 11:36:13 GMT
|
50
|
+
- request:
|
51
|
+
method: get
|
52
|
+
uri: https://graph.facebook.com/150352985174571_517105121832687?access_token=<CLIENT_ID>%7C0WiNEz4rbcf5Ki0Ge4vR7m4Wlts&fields=name,message,updated_time,created_time,attachments%7Btitle,description,url,media%7D
|
53
|
+
body:
|
54
|
+
encoding: US-ASCII
|
55
|
+
string: ''
|
56
|
+
headers:
|
57
|
+
Connection:
|
58
|
+
- close
|
59
|
+
Host:
|
60
|
+
- graph.facebook.com
|
61
|
+
User-Agent:
|
62
|
+
- http.rb/2.0.3
|
63
|
+
response:
|
64
|
+
status:
|
65
|
+
code: 200
|
66
|
+
message: OK
|
67
|
+
headers:
|
68
|
+
Access-Control-Allow-Origin:
|
69
|
+
- "*"
|
70
|
+
Etag:
|
71
|
+
- '"19ab4fac7a978265ff28a3f4c89a24f3a37265f4"'
|
72
|
+
Pragma:
|
73
|
+
- no-cache
|
74
|
+
Cache-Control:
|
75
|
+
- private, no-cache, no-store, must-revalidate
|
76
|
+
Facebook-Api-Version:
|
77
|
+
- v2.7
|
78
|
+
Expires:
|
79
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
80
|
+
Content-Type:
|
81
|
+
- text/javascript; charset=UTF-8
|
82
|
+
X-Fb-Trace-Id:
|
83
|
+
- HUt5ZGrTYkm
|
84
|
+
X-Fb-Rev:
|
85
|
+
- '2654373'
|
86
|
+
Vary:
|
87
|
+
- Accept-Encoding
|
88
|
+
X-Fb-Debug:
|
89
|
+
- zvEa/+ZE+NkLMj6+4VJpKOuYmfhO3qHrj+qqxO990wx53Ttsv/b1/wg5C/LQ4Ci10m70DkDMwJLeIEsKvModtA==
|
90
|
+
Date:
|
91
|
+
- Mon, 31 Oct 2016 11:36:32 GMT
|
92
|
+
Connection:
|
93
|
+
- close
|
94
|
+
body:
|
95
|
+
encoding: UTF-8
|
96
|
+
string: '{"name":"Guess the Programming Language | Tutorialzine","message":"A
|
97
|
+
little quiz to keep you sharp on programming trends.","updated_time":"2016-10-06T06:51:44+0000","created_time":"2016-10-04T02:21:25+0000","attachments":{"data":[{"title":"Guess
|
98
|
+
the Programming Language | Tutorialzine","description":"Can you tell Java
|
99
|
+
from JavaScript? Awesome! So you are ready to take our ultimate programming
|
100
|
+
challenge.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Ftutorialzine.com\u00252F2014\u00252F06\u00252Fguess-the-programming-language\u00252F&h=ZAQFN2caD&s=1&enc=AZOSnK148OZJedVdYW4wBMXBd3PXCWbCnSv00om_hEzZx_-VSjNESrSgpmRpy0iGYn_kc_x85BOz70FuPyrkJwRx","media":{"image":{"height":490,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQB9saA-D40lNQqm&w=720&h=720&url=http\u00253A\u00252F\u00252Fcdn.tutorialzine.com\u00252Fwp-content\u00252Fuploads\u00252F2014\u00252F06\u00252Fguess-the-programming-language.jpg&cfs=1","width":490}}}]},"id":"150352985174571_517105121832687"}'
|
101
|
+
http_version:
|
102
|
+
recorded_at: Mon, 31 Oct 2016 11:36:13 GMT
|
103
|
+
- request:
|
104
|
+
method: get
|
105
|
+
uri: https://graph.facebook.com/150352985174571?access_token=<CLIENT_ID>%7C0WiNEz4rbcf5Ki0Ge4vR7m4Wlts&fields=id,name,feed%7Bname,message,updated_time,created_time,attachments%7Btitle,description,url,media%7D%7D
|
106
|
+
body:
|
107
|
+
encoding: US-ASCII
|
108
|
+
string: ''
|
109
|
+
headers:
|
110
|
+
Connection:
|
111
|
+
- close
|
112
|
+
Host:
|
113
|
+
- graph.facebook.com
|
114
|
+
User-Agent:
|
115
|
+
- http.rb/2.0.3
|
116
|
+
response:
|
117
|
+
status:
|
118
|
+
code: 200
|
119
|
+
message: OK
|
120
|
+
headers:
|
121
|
+
Access-Control-Allow-Origin:
|
122
|
+
- "*"
|
123
|
+
Etag:
|
124
|
+
- '"1553fa0c64c395667251b3f53cd905ba13401d99"'
|
125
|
+
Pragma:
|
126
|
+
- no-cache
|
127
|
+
Cache-Control:
|
128
|
+
- private, no-cache, no-store, must-revalidate
|
129
|
+
Facebook-Api-Version:
|
130
|
+
- v2.7
|
131
|
+
Expires:
|
132
|
+
- Sat, 01 Jan 2000 00:00:00 GMT
|
133
|
+
Content-Type:
|
134
|
+
- text/javascript; charset=UTF-8
|
135
|
+
X-Fb-Trace-Id:
|
136
|
+
- CeJKdwfDcKG
|
137
|
+
X-Fb-Rev:
|
138
|
+
- '2654373'
|
139
|
+
Vary:
|
140
|
+
- Accept-Encoding
|
141
|
+
X-Fb-Debug:
|
142
|
+
- bND+vSntpGwMwBNkp4tYgQrDACRYLI8qI/IKmVX4iuC6SjaMtuFoyb6UsMLkI21NWYpd5GV0GjeUm1Qu1bB47g==
|
143
|
+
Date:
|
144
|
+
- Mon, 31 Oct 2016 11:36:33 GMT
|
145
|
+
Connection:
|
146
|
+
- close
|
147
|
+
body:
|
148
|
+
encoding: UTF-8
|
149
|
+
string: '{"id":"150352985174571","name":"Web Service Development \u0040 NTHU","feed":{"data":[{"name":"seriot.ch
|
150
|
+
- Parsing JSON is a Minefield \ud83d\udca3","message":"From the article: \"JSON
|
151
|
+
is not a data format you can rely on blindly [...] edge cases and maliciously
|
152
|
+
crafted payloads can cause bugs, crashes and denial of services, mainly because
|
153
|
+
JSON libraries rely on specifications that have evolved over time and that
|
154
|
+
left many details loosely specified or not specified at all.\" Long story
|
155
|
+
short: it seems XML will never die :D (actually, parsing XML is even worse...)","updated_time":"2016-10-28T02:09:39+0000","created_time":"2016-10-27T14:46:58+0000","id":"150352985174571_527664860776713","attachments":{"data":[{"title":"seriot.ch
|
156
|
+
- Parsing JSON is a Minefield \ud83d\udca3","description":"JSON is the de
|
157
|
+
facto standard when it comes to (un)serialising and exchanging data in web
|
158
|
+
and mobile programming. But how well do you really know JSON? We''ll read
|
159
|
+
the specifications and write test cases together. We''ll test common JSON
|
160
|
+
libraries against our test cases. I''ll show that JSON is not the...","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fseriot.ch\u00252Fparsing_json.html&h=pAQGbdY1U&s=1&enc=AZOhZ_DoV8AZYnRVVBTklmcENxS7G53vn-lEU4ru_i3oXEgCxJnmbIOAWJvB-Gu3BWDU2GMBqx2nT6SzFh_zhGzU","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBafep0D8nqgNS7&w=720&h=720&url=http\u00253A\u00252F\u00252Fseriot.ch\u00252Fjson\u00252Fpruned_results.png&cfs=1","width":720}}}]}},{"name":"GitHub
|
161
|
+
Dumps REST Calls for Facebook''s GraphQL - The New Stack","message":"Github''s
|
162
|
+
adoption of GraphQL for APIs, over RESTful services, might be the tipping
|
163
|
+
point that makes GraphQL ubiquitous. From what little exposure I have to it
|
164
|
+
(thanks to YenTing and Yen-Nan at Cardinal Blue), it seems we are taking the
|
165
|
+
complexity and burden of APIs away from API consumers, and putting it squarely
|
166
|
+
at the feet of API providers. That means using APIs is about to become significantly
|
167
|
+
easier (albeit more deeply nested) for everyone! But it also means that creating
|
168
|
+
APIs is about to become significantly harder. Read more about the big shift
|
169
|
+
at Github.","updated_time":"2016-10-20T08:29:06+0000","created_time":"2016-10-20T05:45:32+0000","id":"150352985174571_524070464469486","attachments":{"data":[{"title":"GitHub
|
170
|
+
Dumps REST Calls for Facebook''s GraphQL - The New Stack","description":"Hoping
|
171
|
+
to streamline the remote querying services of its site, GitHub is moving its
|
172
|
+
API (application programming interface) from REST calls to Facebook\u2019s
|
173
|
+
GraphQL API. In the company\u2019s recent user conference GitHub Universe,
|
174
|
+
Kyle Daigle, GitHub platform engineering manager, explained that the company...","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fthenewstack.io\u00252Fgithub-dumps-rest-graphql-api\u00252F&h=5AQEXy6ZK&s=1&enc=AZP7SMCQUn4mPXq0u-5YrVbTOhpXUuU-7BEiY2T9VXvmMvx6DLVMgb7u7K0aQDR8WXQINoKi33_MKcXbrZe3T4tk","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQC5HLw3s46cLqn7&w=720&h=720&url=http\u00253A\u00252F\u00252Fthenewstack.io\u00252Fwp-content\u00252Fuploads\u00252F2016\u00252F09\u00252FGHU-GraphQL-Woah.jpg&cfs=1&sx=0&sy=0&sw=2448&sh=2448","width":720}}}]}},{"name":"Taking
|
175
|
+
PHP Seriously","message":"https:\/\/m.facebook.com\/story.php?story_fbid=1098448240203391&id=100001146751327","updated_time":"2016-10-13T18:17:23+0000","created_time":"2016-10-13T17:15:20+0000","id":"150352985174571_521137638096102","attachments":{"data":[{"title":"Taking
|
176
|
+
PHP Seriously","description":"by Keith Adams, Slack Engineering","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fslack.engineering\u00252Ftaking-php-seriously-cf7a60065329\u002523.g3w9ecf66&h=5AQEXy6ZK&s=1&enc=AZOPuZs61zyxp-EAxBN3BJMYh36_YVKmxt02IWhV_f_uldh2ENrSMUA1imFjIGcuh_tDtSEmqzenhxGjzQYH1mK5","media":{"image":{"height":669,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCmbxAdG46sLBUD&w=720&h=720&url=https\u00253A\u00252F\u00252Fd262ilb51hltx0.cloudfront.net\u00252Fmax\u00252F1200\u00252F1\u00252Ax5N8tfTVEmQro2ySxoZtvw.jpeg&cfs=1","width":669}}}]}},{"name":"How
|
177
|
+
To Save The Princess In 8 Programming Languages","updated_time":"2016-10-06T08:39:47+0000","created_time":"2016-10-06T08:28:40+0000","id":"150352985174571_518029841740215","attachments":{"data":[{"title":"How
|
178
|
+
To Save The Princess In 8 Programming Languages","description":"Programming
|
179
|
+
sucks. This fairytale comic shows the different ways in which programming
|
180
|
+
languages could fail you in the fantasy kingdom.","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Ftoggl.com\u00252Fprogramming-princess&h=yAQECYco_&s=1&enc=AZOrSP3Z5tmWrh10hw6WLxnqSBrUUe6GhApxOB6KFhM12_JwPOdXe6QzrPpUd_0q2zEV3OPa_u10ZWuF7whJrt8F","media":{"image":{"height":630,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQDEZQJwFjW82Fzm&w=720&h=720&url=https\u00253A\u00252F\u00252Ftoggl.com\u00252Fimages\u00252Fshare-img\u00252Ffb-share-git-princess.jpg&cfs=1","width":630}}}]}},{"name":"Guess
|
181
|
+
the Programming Language | Tutorialzine","message":"A little quiz to keep
|
182
|
+
you sharp on programming trends.","updated_time":"2016-10-06T06:51:44+0000","created_time":"2016-10-04T02:21:25+0000","id":"150352985174571_517105121832687","attachments":{"data":[{"title":"Guess
|
183
|
+
the Programming Language | Tutorialzine","description":"Can you tell Java
|
184
|
+
from JavaScript? Awesome! So you are ready to take our ultimate programming
|
185
|
+
challenge.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Ftutorialzine.com\u00252F2014\u00252F06\u00252Fguess-the-programming-language\u00252F&h=NAQFG7_It&s=1&enc=AZP9HQ5mcmmfcwfFcEr5CoDl16o34wUhGiKl50GeDr7D2dSVFv3OG8Dkzab_o9qcSuL5JyhXYON4P1Oqmn-i21qP","media":{"image":{"height":490,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQB9saA-D40lNQqm&w=720&h=720&url=http\u00253A\u00252F\u00252Fcdn.tutorialzine.com\u00252Fwp-content\u00252Fuploads\u00252F2014\u00252F06\u00252Fguess-the-programming-language.jpg&cfs=1","width":490}}}]}},{"name":"How
|
186
|
+
it feels to learn JavaScript in 2016","updated_time":"2016-10-06T01:18:12+0000","created_time":"2016-10-05T13:47:43+0000","id":"150352985174571_517701838439682","attachments":{"data":[{"title":"How
|
187
|
+
it feels to learn JavaScript in 2016","description":"Edit: Thanks for pointing
|
188
|
+
typos and mistakes, I\u2019ll update the article as noted. Discussion in HackerNews
|
189
|
+
and Reddit.","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fhackernoon.com\u00252Fhow-it-feels-to-learn-javascript-in-2016-d3a717dd577f\u002523.m0lkkfan1&h=-AQFaxps1&s=1&enc=AZOZpUPmyiTSwAY2laOzQuSKnTkTgsbfQ2qKwbavuXxu1Pc5zTci7eN9N617ApwnAFptH5HFAemnwYqyHOfwZkWD","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBw5xIxnMhHtpnj&w=720&h=720&url=https\u00253A\u00252F\u00252Fcdn-images-2.medium.com\u00252Fmax\u00252F1200\u00252F1\u00252AraWO3dhM4jMjf9VY-kZzNg.png&cfs=1","width":720}}}]}},{"name":"5
|
190
|
+
emerging programming languages with a bright future","message":"Up and coming
|
191
|
+
languages worth keeping our eyes on. I''m already a huge fan of Crystal and
|
192
|
+
Rust, but the author has convinced me to pay more attention to Kotlin and
|
193
|
+
Elm.","updated_time":"2016-09-22T08:12:35+0000","created_time":"2016-09-21T10:13:58+0000","id":"150352985174571_511894255687107","attachments":{"data":[{"title":"5
|
194
|
+
emerging programming languages with a bright future","description":"Get the
|
195
|
+
elevator pitch for five of the most promising languages (some of them you
|
196
|
+
probably haven''t heard of before) that have the potential to grow...","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Ftechbeacon.com\u00252F5-emerging-programming-languages-bright-future\u00253Futm_content\u00253Dbuffer300a1\u002526utm_medium\u00253Dsocial\u002526utm_source\u00253Dtwitter.com\u002526utm_campaign\u00253Dbuffer&h=dAQFB3XXZ&s=1&enc=AZOBfNpM4UtkySbx0bWAnVmDzM9N1dQ9o2nIMMpjYKzTCmsK0PLlC9BIwtPPwb8KUsXwg9PZWArNndqUN2DQiX4g","media":{"image":{"height":480,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQAxbtCPHht3vWb6&w=720&h=720&url=http\u00253A\u00252F\u00252Ftechbeacon.com\u00252Fsites\u00252Fdefault\u00252Ffiles\u00252Fstyles\u00252Fsocial\u00252Fpublic\u00252Ffield\u00252Fimage\u00252F5_emerging_programming_languages_with_a_bright_future.jpg\u00253Fitok\u00253Dm0nlEinF&cfs=1","width":480}}}]}},{"name":"A
|
197
|
+
whole new Universe","message":"I can''t even wrap my head around all the new
|
198
|
+
features Github just released: code reviews, kanban boards, and much more.","updated_time":"2016-09-19T09:14:51+0000","created_time":"2016-09-15T03:42:48+0000","id":"150352985174571_509461352597064","attachments":{"data":[{"title":"A
|
199
|
+
whole new Universe","description":"Learn about the exciting features and announcements
|
200
|
+
revealed at this year\u2019s annual GitHub Universe conference at historic
|
201
|
+
Pier 70 in San Francisco.","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fgithub.com\u00252Funiverse-2016&h=IAQHwFDmG&s=1&enc=AZNxDuwEVODTaUs6Do2WpBEGpRIOJymUaF5YfO69uS5q76v6eJWMVSylBnk9T5plhBflgibCmsV875OJuaDRtfW2","media":{"image":{"height":630,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBsb70KV75vMJ8B&w=720&h=720&url=https\u00253A\u00252F\u00252Fassets-cdn.github.com\u00252Fimages\u00252Fmodules\u00252Funiverse-2016\u00252Funiverse-open-graph.png&cfs=1","width":630}}}]}},{"name":"Vert.x","message":"http:\/\/vertx.io\/","updated_time":"2016-09-13T20:34:39+0000","created_time":"2016-09-13T20:34:39+0000","id":"150352985174571_508953969314469","attachments":{"data":[{"title":"Vert.x","description":"Vert.x
|
202
|
+
is a tool-kit for building reactive applications on the JVM.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fvertx.io\u00252F&h=sAQFM4S68&s=1&enc=AZN4Iv2jN7Y0kZuKuQNprjSOv9OioIG94uFZSkhYcxPxqauoTOYZCxhuI6PzdbsNwMm1vH2MOhj13jMPAQn0bPib","media":{"image":{"height":360,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQC13D8UqDGMX5IF&w=720&h=720&url=http\u00253A\u00252F\u00252Fvertx.io\u00252Fassets\u00252Fuser_logos\u00252Fswisscom.png&cfs=1","width":360}}}]}},{"name":"Codecademy","updated_time":"2016-09-13T02:04:20+0000","created_time":"2016-09-13T02:04:20+0000","id":"150352985174571_508631766013356","attachments":{"data":[{"title":"Timeline
|
203
|
+
Photos","description":"Are you a Javascript guru? Then you should check out
|
204
|
+
our course in React.js!\n\nReact enables websites to display complex animations,
|
205
|
+
large volumes of data, or other memory-heavy tasks without slowing down. At
|
206
|
+
the end of the course, you\u2019ll know how to build sophisticated React applications.
|
207
|
+
Check it out!\n\nhttp:\/\/codecademy.io\/YRFS3047CDf","url":"https:\/\/www.facebook.com\/codecademy\/photos\/a.553205214692855.138303.272256069454439\/1387442691269099\/?type=3","media":{"image":{"height":377,"src":"https:\/\/scontent.xx.fbcdn.net\/v\/t1.0-9\/s720x720\/14264951_1387442691269099_7557697962736020966_n.png?oh=1c67604ccd5679d98c72100f943f3863&oe=5891713F","width":720}}}]}},{"name":"Scaling
|
208
|
+
Spinnaker at Netflix \u2014 The Basics","message":"https:\/\/medium.com\/\u0040ajordens\/scaling-spinnaker-at-netflix-part-1-8a5ae51ee6de#.fubo771g2","updated_time":"2016-09-11T09:14:53+0000","created_time":"2016-09-11T09:14:53+0000","id":"150352985174571_507894159420450","attachments":{"data":[{"title":"Scaling
|
209
|
+
Spinnaker at Netflix \u2014 The Basics","description":"The Backstory","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fmedium.com\u00252F\u002540ajordens\u00252Fscaling-spinnaker-at-netflix-part-1-8a5ae51ee6de\u002523.fubo771g2&h=pAQGbdY1U&s=1&enc=AZMdr5SvBS9yxRKqZr-6VinF_uvE-TI3ci6lBT6EEUNGaKpwXEE5QJiVrWKcVWMNTCSS2GMMJkDyImxEw3Qryzc1","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBS3PgL3uUEU1VC&w=720&h=720&url=https\u00253A\u00252F\u00252Fcdn-images-1.medium.com\u00252Fmax\u00252F1200\u00252F1\u00252AMrL6DHIxxY6CrL9Binpjjg.png&cfs=1","width":720}}}]}},{"name":"Design
|
210
|
+
Stamina Hypothesis","message":"Why (and when) we need architecture.","updated_time":"2016-09-08T03:33:06+0000","created_time":"2015-09-24T11:12:32+0000","id":"150352985174571_390601061149761","attachments":{"data":[{"title":"Design
|
211
|
+
Stamina Hypothesis","description":"The value of good software design is economic:
|
212
|
+
you can continue to add new functionality quickly even as the code-base grows
|
213
|
+
in size.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fmartinfowler.com\u00252Fbliki\u00252FDesignStaminaHypothesis.html&h=hAQEjWdH5&s=1&enc=AZPBRPGZ8n54geMOCiZlRvCnKbjF5yCxbP74wx8H18deII1uhZc_goBd-sPC9IR9WSJllfFUvdIh9FvE2ZpELnV6","media":{"image":{"height":329,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQB61p6nJsutcaQ4&w=720&h=720&url=http\u00253A\u00252F\u00252Fmartinfowler.com\u00252Fbliki\u00252Fimages\u00252FdesignStaminaGraph.gif&cfs=1","width":329}}}]}},{"name":"Git
|
214
|
+
2.10 has been released","updated_time":"2016-09-03T06:33:51+0000","created_time":"2016-09-03T05:23:56+0000","id":"150352985174571_504539303089269","attachments":{"data":[{"title":"Git
|
215
|
+
2.10 has been released","description":"The open source Git project has just
|
216
|
+
released Git 2.10.0, with features and bugfixes from over 70 contributors.
|
217
|
+
Here''s our look at some of the most interesting new features: Progress reporting
|
218
|
+
for...","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fgithub.com\u00252Fblog\u00252F2242-git-2-10-has-been-released&h=dAQFB3XXZ&s=1&enc=AZOQouuSRO25XYW6K3kxpSaLQ0w1BYVGK6PXqm0MQAW5x-mcQ4knCPixp3POXCLCyGAtuk9a6_TonM9WQVEsI7-5","media":{"image":{"height":630,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQAMeAXoLqWgJ6YI&w=720&h=720&url=https\u00253A\u00252F\u00252Fcloud.githubusercontent.com\u00252Fassets\u00252F121322\u00252F16134794\u00252F284be2e6-33d4-11e6-9165-58068a872ec2.png&cfs=1","width":630}}}]}},{"name":"The
|
219
|
+
target=\"_blank\" vulnerability by example","message":"https:\/\/dev.to\/ben\/the-targetblank-vulnerability-by-example","updated_time":"2016-09-02T17:50:31+0000","created_time":"2016-09-01T12:49:45+0000","id":"150352985174571_503746883168511","attachments":{"data":[{"title":"The
|
220
|
+
target=\"_blank\" vulnerability by example","description":"Instagram leaves
|
221
|
+
its users open to a simple phishing attack","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fdev.to\u00252Fben\u00252Fthe-targetblank-vulnerability-by-example&h=AAQFBgNq5&s=1&enc=AZOF9BILo6btwhKgaMMCeACe7TncZqjzLtl5rhxXLGg7HzhJW0YPsGbsWvuKlXrYXpONKkOOqNgHQ0wC-2woHECd","media":{"image":{"height":700,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCuBI-PnC7cvAsc&w=720&h=720&url=https\u00253A\u00252F\u00252Fres.cloudinary.com\u00252Fpracticaldev\u00252Fimage\u00252Ffetch\u00252Fs--AGTf6Osi--\u00252Fc_imagga_scale\u00252Cf_auto\u00252Cfl_progressive\u00252Ch_700\u00252Cq_auto\u00252Cw_1480\u00252Fhttps\u00253A\u00252F\u00252Fi.vimeocdn.com\u00252Fvideo\u00252F570148251_1280x720.jpg&cfs=1","width":700}}}]}},{"name":"Katacoda
|
222
|
+
- Interactive Learning Platform for Software Engineers","message":"just discovered
|
223
|
+
this .... looks nice","updated_time":"2016-08-26T02:34:28+0000","created_time":"2016-08-26T02:34:28+0000","id":"150352985174571_500972406779292","attachments":{"data":[{"title":"Katacoda
|
224
|
+
- Interactive Learning Platform for Software Engineers","description":"Learn
|
225
|
+
the latest technologies with our hands-on labs","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fwww.katacoda.com\u00252F&h=ZAQEZa7yF&s=1&enc=AZOjDRfERJ493aK44k3JuzYkj9f6Gw9ANO_K1Nxn4F2sozR4ahvy-mjFtbH5dsY5POdHNouHWhxB_jJKbBZme4BP","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQBt33FvO3-PQ4vI&w=720&h=720&url=https\u00253A\u00252F\u00252Fwww.katacoda.com\u00252Fimages\u00252Fbackgrounds\u00252Fpeople-working.jpg&cfs=1","width":720}}}]}},{"name":"Let''s
|
226
|
+
Encrypt - Wikipedia, the free encyclopedia","message":"No reason not to use
|
227
|
+
https for your web services: apart from the good old https:\/\/www.startssl.com,
|
228
|
+
now we have https:\/\/letsencrypt.org which makes getting, installing and
|
229
|
+
keeping a cert up to date easier. Plus its backed by many big guns and its
|
230
|
+
FREE! Here is the wikipedia article about it: https:\/\/en.m.wikipedia.org\/wiki\/Let\u002527s_Encrypt.
|
231
|
+
Just remember its pretty new and do read about how the certs are signed and
|
232
|
+
accepted by browser\/clients.","updated_time":"2016-08-22T00:55:29+0000","created_time":"2016-08-21T09:50:36+0000","id":"150352985174571_499005640309302","attachments":{"data":[{"title":"Let''s
|
233
|
+
Encrypt - Wikipedia, the free encyclopedia","description":"Let''s Encrypt
|
234
|
+
is a certificate authority that launched on April 12, 2016[1][2] that provides
|
235
|
+
free X.509 certificates for Transport Layer Security (TLS) encryption via
|
236
|
+
an automated process designed to eliminate the current complex process of
|
237
|
+
manual creation, validation, signing, installation, and ren...","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fen.m.wikipedia.org\u00252Fwiki\u00252FLet\u00252527s_Encrypt&h=6AQH4hGaj&s=1&enc=AZNOOEWW4ZyzpwN2bxvv43OHUnLLLwDMK5OTlRaBWrQALmUwqVMLyTvKhpSI0H0-tPnX3Tx0e_NowHbRx1QeC_08","media":{"image":{"height":131,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQApCjwHGpTUJRSa&w=720&h=720&url=https\u00253A\u00252F\u00252Fupload.wikimedia.org\u00252Fwikipedia\u00252Fcommons\u00252Fthumb\u00252Fb\u00252Fbb\u00252FLetsencrypt_screenshot_2_domain_choice.png\u00252F220px-Letsencrypt_screenshot_2_domain_choice.png&cfs=1","width":131}}}]}},{"name":"Google''s
|
238
|
+
QUIC protocol: moving the web from TCP to UDP","message":"A speedier web?
|
239
|
+
Yes thank you!\n\n","updated_time":"2016-08-02T04:40:01+0000","created_time":"2016-08-01T13:53:37+0000","id":"150352985174571_491254161084450","attachments":{"data":[{"title":"Google''s
|
240
|
+
QUIC protocol: moving the web from TCP to UDP","description":"The QUIC protocol
|
241
|
+
(Quick UDP Internet Connections) is an entirely new protocol for the web developed
|
242
|
+
on top of UDP instead of TCP. Some are even (jokingly) calling it TCP\/2.
|
243
|
+
I only learned about QUIC a few weeks ago while doing the curl & libcurl episode
|
244
|
+
of the SysCast podcast. The really interestin...","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fma.ttias.be\u00252Fgoogles-quic-protocol-moving-web-tcp-udp\u00252F&h=vAQGfGT4c&s=1&enc=AZPJrlg3YS3iKVvWZWUwT-YUh3CsfyPvg_YgQvTPa9T4qtHEm9j6PIXT6Nvwwgd7KHgamaKo1N_86KDHwYU4SMsb","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQAAP49rj-fnvioY&w=720&h=720&url=https\u00253A\u00252F\u00252Fma.ttias.be\u00252Fwp-content\u00252Fuploads\u00252F2016\u00252F07\u00252Fquic_parking_lot_problem.png&cfs=1","width":720}}}]}},{"name":"Introducing
|
245
|
+
GitKraken Pro\u2014launch fundraiser | Axosoft","message":"https:\/\/blog.axosoft.com\/2016\/07\/28\/introducing-gitkraken-pro\/","updated_time":"2016-07-30T17:51:41+0000","created_time":"2016-07-30T17:51:41+0000","id":"150352985174571_490544944488705","attachments":{"data":[{"title":"Introducing
|
246
|
+
GitKraken Pro\u2014launch fundraiser | Axosoft","description":"Learn about
|
247
|
+
the new edition of GitKraken, with even more features, and help raise money
|
248
|
+
and awareness for the Nightscout Foundation, an open source technology project
|
249
|
+
helping those affected by Type 1 diabetes!","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Fblog.axosoft.com\u00252F2016\u00252F07\u00252F28\u00252Fintroducing-gitkraken-pro\u00252F&h=dAQFB3XXZ&s=1&enc=AZOwunEYog8Fk1470qxi4Pj8ud_hJmUh_N7vZW_mjR0Wv-KIIy3iTHWG6eMlPsZpbBUKNt_hSjsTaOORu4K_k2qY","media":{"image":{"height":720,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCiRe3ytesqyg6K&w=720&h=720&url=https\u00253A\u00252F\u00252Fblog.axosoft.com\u00252Fwp-content\u00252Fuploads\u00252F2016\u00252F07\u00252FTeaming-Up-v2-wide.png&cfs=1&l","width":720}}}]}},{"name":"Create
|
250
|
+
Apps with No Configuration | React","message":"For those using React, although
|
251
|
+
optional webpack loaders (such as sass) still need to be added!","updated_time":"2016-07-23T10:41:04+0000","created_time":"2016-07-23T09:55:25+0000","id":"150352985174571_487752758101257","attachments":{"data":[{"title":"Create
|
252
|
+
Apps with No Configuration | React","description":"A JavaScript library for
|
253
|
+
building user interfaces","url":"https:\/\/www.facebook.com\/l.php?u=https\u00253A\u00252F\u00252Ffacebook.github.io\u00252Freact\u00252Fblog\u00252F2016\u00252F07\u00252F22\u00252Fcreate-apps-with-no-configuration.html&h=zAQHHar3S&s=1&enc=AZMzd6n2Emq956imUxkM2P73QsA-jsr1j9-QrpxJYTjHYi1EXLTV7vtAXcGyiJQuUAwQBHhbvCz0RVSYuFvrg_M_","media":{"image":{"height":400,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQC26d6oRyFdqvxX&w=720&h=720&url=https\u00253A\u00252F\u00252Ffacebook.github.io\u00252Freact\u00252Fimg\u00252Flogo_og.png&cfs=1","width":400}}}]}},{"name":"introduction\u00b6
|
254
|
+
Lettuce is an extremely useful and charming tool for BDD (Behavior Driven...
|
255
|
+
-...","message":"Test scenarios that anyone can make sense of","updated_time":"2016-07-22T11:46:59+0000","created_time":"2016-07-22T06:28:02+0000","id":"150352985174571_487315358144997","attachments":{"data":[{"title":"introduction\u00b6
|
256
|
+
Lettuce is an extremely useful and charming tool for BDD (Behavior Driven...
|
257
|
+
-...","description":"Lettuce is an extremely useful and charming tool for
|
258
|
+
BDD (Behavior Driven Development). It can execute plain-text functional descriptions
|
259
|
+
as automated tests for Python projects, just as Cucumber does for Ruby.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Flettuce.it\u00252Ftutorial\u00252Fsimple.html\u002523tutorial-simple&h=QAQHJzSDZ&s=1&enc=AZNXEe-FCkziOS4W3zMOMsPDlvRbqtWsZl8isTCHhydlF0iwFGSWk2gg2Iwl_YUO1vDYGIM2ddsT3W2q07mYD-Od","media":{"image":{"height":687,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQDL7gIjkOiwMbE7&w=720&h=720&url=http\u00253A\u00252F\u00252Flettuce.it\u00252F_images\u00252Fscreenshot5.png&cfs=1","width":687}}}]}},{"message":"Check
|
260
|
+
out: https:\/\/www.websequencediagrams.com \u2014 fun and friendly way to
|
261
|
+
create sequence diagrams for web and mobile activities. I usually end up drawing
|
262
|
+
these on paper or on the board. I think it will be quite engaging to play
|
263
|
+
with this live!","updated_time":"2016-07-21T02:25:49+0000","created_time":"2016-07-20T03:59:51+0000","id":"150352985174571_486566974886502","attachments":{"data":[{"description":"Check
|
264
|
+
out: https:\/\/www.websequencediagrams.com \u2014 fun and friendly way to
|
265
|
+
create sequence diagrams for web and mobile activities. I usually end up drawing
|
266
|
+
these on paper or on the board. I think it will be quite engaging to play
|
267
|
+
with this live!","url":"https:\/\/www.facebook.com\/photo.php?fbid=1058362510919499&set=gm.486566974886502&type=3","media":{"image":{"height":392,"src":"https:\/\/scontent.xx.fbcdn.net\/v\/t1.0-9\/s720x720\/13680904_1058362510919499_8542835077177246391_n.jpg?oh=e23aa643f32eb9665e3c8ba8a43c01ca&oe=58A0A9E7","width":720}}}]}},{"name":"Dropbox
|
268
|
+
open-sources Lepton, a compression algorithm that cuts JPEG file size by 22\u0025","message":"http:\/\/venturebeat.com\/2016\/07\/14\/dropbox-open-sources-lepton-a-compression-algorithm-that-cuts-jpeg-file-size-by-22\/","updated_time":"2016-07-15T05:52:51+0000","created_time":"2016-07-14T19:37:54+0000","id":"150352985174571_484513371758529","attachments":{"data":[{"title":"Dropbox
|
269
|
+
open-sources Lepton, a compression algorithm that cuts JPEG file size by 22\u0025","description":"Cloud
|
270
|
+
syncing and sharing software company Dropbox today announced that it has released
|
271
|
+
an image compression algorithm called Lepton under an Apache open-source license
|
272
|
+
on GitHub.","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fventurebeat.com\u00252F2016\u00252F07\u00252F14\u00252Fdropbox-open-sources-lepton-a-compression-algorithm-that-cuts-jpeg-file-size-by-22\u00252F&h=JAQEPnFK0&s=1&enc=AZMjmVzr_G1gBKkY7nUuDz6AxY955tDBPQ4JEB35D2opKc0txgXlVfJngJYiHgqqtNQ7VimeUoNXbYHjNm8oXvBn","media":{"image":{"height":585,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCBxul8ygkf06Je&w=720&h=720&url=https\u00253A\u00252F\u00252Fventurebeat.com\u00252Fwp-content\u00252Fuploads\u00252F2015\u00252F11\u00252FDropbox-Open-outside-Novet-780x585.jpg&cfs=1","width":585}}}]}},{"name":"Serverless
|
273
|
+
Architectures","message":"\"Serverless\" is the new sensation in service architecture,
|
274
|
+
and it integrates the recent trends of microservices and reactive (message-driven)
|
275
|
+
architectures. We will play with some of these concepts in our SOA class this
|
276
|
+
coming semester. Take a peek at what''s brewing.\nhttp:\/\/martinfowler.com\/articles\/serverless.html","updated_time":"2016-07-14T19:01:17+0000","created_time":"2016-07-14T12:16:33+0000","id":"150352985174571_484366188439914","attachments":{"data":[{"title":"Serverless
|
277
|
+
Architectures","description":"Serverless architectures replace a managed server
|
278
|
+
with a collection of third party services and FaaS","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fmartinfowler.com\u00252Farticles\u00252Fserverless.html\u002523benefits&h=tAQFxOLUP&s=1&enc=AZOnodmObsKCua5Hgv0LB-cV9d94ECVdGjy7gm3yqARhHQqWT7Ojra2PH27QqIwjswS5aEk-CzEIvvUItmig30Mv","media":{"image":{"height":300,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCGBUsGoj1TcRRD&w=720&h=720&url=http\u00253A\u00252F\u00252Fmartinfowler.com\u00252Farticles\u00252Fserverless\u00252Fsps.png&cfs=1","width":300}}}]}},{"name":"Pokemon
|
279
|
+
Go Is Driving Insane Amounts of Sales at Small Local Businesses. Here''s How
|
280
|
+
It Works","message":"I''m shocked at how Pok\u00e9mon Go is taking over our
|
281
|
+
streets (very soon in TW, right?). And now its having a major effect on local
|
282
|
+
businesses as well. Great example of how an online service can transform offline
|
283
|
+
ones.","updated_time":"2016-07-13T09:36:32+0000","created_time":"2016-07-13T02:42:08+0000","id":"150352985174571_483847351825131","attachments":{"data":[{"title":"Pokemon
|
284
|
+
Go Is Driving Insane Amounts of Sales at Small Local Businesses. Here''s How
|
285
|
+
It Works","description":"For $1.19 an hour, you can have more customers than
|
286
|
+
you''ve ever seen in your life","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fwww.inc.com\u00252Fwalter-chen\u00252Fpok-mon-go-is-driving-insane-amounts-of-sales-at-small-local-businesses-here-s-h.html&h=0AQH76qxR&s=1&enc=AZPeGp9RV6xgmO8IJ7b6-l54PweT1SvXcMy-riyqyk7b8D_XJTeZB4ygHuSN3wvVARUuuAOegMhkKkup0KGeS94-","media":{"image":{"height":450,"src":"https:\/\/external.xx.fbcdn.net\/safe_image.php?d=AQCDr9LKzFIQfJFh&w=720&h=720&url=http\u00253A\u00252F\u00252Fwww.incimages.com\u00252Fuploaded_files\u00252Fimage\u00252F970x450\u00252FPokemon_Heroes_2003_03-cc_100058.jpg&cfs=1","width":450}}}]}},{"name":"SOA
|
287
|
+
Syllabus","message":"The syllabus for next semester''s \"Service Oriented
|
288
|
+
Architecture\" class is now online! Take a look and share it with your coder
|
289
|
+
friends who are dreaming of their first startup :)","updated_time":"2016-07-04T11:18:26+0000","created_time":"2016-07-04T06:48:44+0000","id":"150352985174571_480686308807902","attachments":{"data":[{"title":"SOA
|
290
|
+
Syllabus","description":"Institute of Service Science, National Tsing Hua
|
291
|
+
University\u000bService Oriented Architecture\u000bFall 2016 Course Duration:
|
292
|
+
Sep 2015 \u2013 Jan 2016\u000bClass Time: Mondays, 9:00am-12:00pm\u000b Instructor:
|
293
|
+
Soumya Ray (soumya.ray\u0040iss.nthu.edu.tw)\u000bAssistant: TBA (\u0040...
|
294
|
+
available on Slack) How do companies lik...","url":"http:\/\/www.facebook.com\/l.php?u=http\u00253A\u00252F\u00252Fbit.ly\u00252Fsoa-syllabus&h=BAQHN6LOh&s=1&enc=AZNGM5gYGMJrxsa388aIW6E8bK0oG-nn_SZUHKtOnATjOkd7hnXIZ_AAH7u4XMHeQP8FKRpOKGc6s1pUR8Tdypp3"}]}}],"paging":{"previous":"https:\/\/graph.facebook.com\/v2.7\/150352985174571\/feed?fields=name,message,updated_time,created_time,attachments\u00257Btitle,description,url,media\u00257D&since=1477620579&access_token=<ACCESS_TOKEN>&limit=25&__paging_token=enc_AdAchevYgL0m5xCIn76hqJ3aBASZBLnnBkHzYtokZBey5PDaQuxdVxZBDfJm2ifeekq5qoALQ6ZA4IMrAhmfWQ5dqvhVBMLvnbwrEWU90LoviZCxtZBgZDZD&__previous=1","next":"https:\/\/graph.facebook.com\/v2.7\/150352985174571\/feed?fields=name,message,updated_time,created_time,attachments\u00257Btitle,description,url,media\u00257D&access_token=<ACCESS_TOKEN>&limit=25&until=1467631106&__paging_token=enc_AdC4X4Wv1EBK4kEN9Y5TOPvPUz3HmgdUXTs7mZCGDzKM0JowQrOJOd0aQmt9j99Ih4aTyPdgZCtSjfotx3noc8yN9dZB40l6MStprBctRTbTZCOQzAZDZD"}}}'
|
295
|
+
http_version:
|
296
|
+
recorded_at: Mon, 31 Oct 2016 11:36:14 GMT
|
297
|
+
recorded_with: VCR 3.0.3
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
group:
|
3
|
+
name: Web Service Development @ NTHU
|
4
|
+
privacy: OPEN
|
5
|
+
id: '150352985174571'
|
6
|
+
posting:
|
7
|
+
created_time: '2016-10-04T02:21:25+0000'
|
8
|
+
message: A little quiz to keep you sharp on programming trends.
|
9
|
+
id: '150352985174571_517105121832687'
|
10
|
+
attachment:
|
11
|
+
- description: Can you tell Java from JavaScript? Awesome! So you are ready to take
|
12
|
+
our ultimate programming challenge.
|
13
|
+
media:
|
14
|
+
image:
|
15
|
+
height: 490
|
16
|
+
src: https://external.xx.fbcdn.net/safe_image.php?d=AQB9saA-D40lNQqm&w=720&h=720&url=http%3A%2F%2Fcdn.tutorialzine.com%2Fwp-content%2Fuploads%2F2014%2F06%2Fguess-the-programming-language.jpg&cfs=1
|
17
|
+
width: 490
|
18
|
+
target:
|
19
|
+
url: http://www.facebook.com/l.php?u=http%3A%2F%2Ftutorialzine.com%2F2014%2F06%2Fguess-the-programming-language%2F&h=QAQFRGe1e&s=1&enc=AZN6y8VgJr9Ergk1McHtlVvCVfRfamxABJTgHWIsRMn-ol4YYMfoBf6AltqD5KJN4lP86EnrEGKSz-Ef6ROz3Lww
|
20
|
+
title: Guess the Programming Language | Tutorialzine
|
21
|
+
type: share
|
22
|
+
url: http://www.facebook.com/l.php?u=http%3A%2F%2Ftutorialzine.com%2F2014%2F06%2Fguess-the-programming-language%2F&h=QAQFRGe1e&s=1&enc=AZN6y8VgJr9Ergk1McHtlVvCVfRfamxABJTgHWIsRMn-ol4YYMfoBf6AltqD5KJN4lP86EnrEGKSz-Ef6ROz3Lww
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'minitest/rg'
|
8
|
+
require 'vcr'
|
9
|
+
require 'webmock'
|
10
|
+
|
11
|
+
require_relative '../lib/facegroups'
|
12
|
+
|
13
|
+
FIXTURES_FOLDER = 'spec/fixtures'
|
14
|
+
CASSETTES_FOLDER = "#{FIXTURES_FOLDER}/cassettes"
|
15
|
+
CASSETTE_FILE = 'facebook_api'
|
16
|
+
|
17
|
+
if File.file?('config/credentials.yml')
|
18
|
+
credentials = YAML.load(File.read('config/credentials.yml'))
|
19
|
+
ENV['FB_CLIENT_ID'] = credentials[:client_id]
|
20
|
+
ENV['FB_CLIENT_SECRET'] = credentials[:client_secret]
|
21
|
+
ENV['FB_ACCESS_TOKEN'] = credentials[:access_token]
|
22
|
+
ENV['FB_GROUP_ID'] = credentials[:group_id]
|
23
|
+
end
|
24
|
+
|
25
|
+
RESULT_FILE = "#{FIXTURES_FOLDER}/fb_api_results.yml"
|
26
|
+
FB_RESULT = YAML.load(File.read(RESULT_FILE))
|
metadata
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: facegroups
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aditya Utama Wijaya
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-31 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.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '11.3'
|
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.42'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0.42'
|
153
|
+
description: Extracts feed, postings, and attachments from FB groups
|
154
|
+
email:
|
155
|
+
- adityautamawijaya@gmail.com
|
156
|
+
executables:
|
157
|
+
- facegroups
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".gitignore"
|
162
|
+
- ".rubocop.yml"
|
163
|
+
- ".travis.yml"
|
164
|
+
- Gemfile
|
165
|
+
- Gemfile.lock
|
166
|
+
- LICENSE
|
167
|
+
- README.md
|
168
|
+
- Rakefile
|
169
|
+
- bin/facegroups
|
170
|
+
- config/credentials.yml.example
|
171
|
+
- facegroups.gemspec
|
172
|
+
- lib/facegroups.rb
|
173
|
+
- lib/facegroups/attachment.rb
|
174
|
+
- lib/facegroups/fb_api.rb
|
175
|
+
- lib/facegroups/feed.rb
|
176
|
+
- lib/facegroups/group.rb
|
177
|
+
- lib/facegroups/posting.rb
|
178
|
+
- lib/facegroups/version.rb
|
179
|
+
- spec/facegroups_spec.rb
|
180
|
+
- spec/fixtures/cassettes/facebook_api.yml
|
181
|
+
- spec/fixtures/fb_api_results.yml
|
182
|
+
- spec/spec_helper.rb
|
183
|
+
homepage: https://github.com/aditya-utama-wijaya/facegroups
|
184
|
+
licenses:
|
185
|
+
- MIT
|
186
|
+
metadata: {}
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
requirements: []
|
202
|
+
rubyforge_project:
|
203
|
+
rubygems_version: 2.5.1
|
204
|
+
signing_key:
|
205
|
+
specification_version: 4
|
206
|
+
summary: Gets posted content from public Facebook groups
|
207
|
+
test_files:
|
208
|
+
- spec/facegroups_spec.rb
|
209
|
+
- spec/fixtures/cassettes/facebook_api.yml
|
210
|
+
- spec/fixtures/fb_api_results.yml
|
211
|
+
- spec/spec_helper.rb
|