facegroups 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/facegroups.svg)](https://badge.fury.io/rb/facegroup)
|
4
|
+
[![Build Status](https://travis-ci.org/aditya-utama-wijaya/facegroups.svg?branch=master)](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
|