squad_goals 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: badf3262d4bdba40aa294a1ae0cda649102b0fe6
4
- data.tar.gz: 7e2cfbed9d2db0c1219ddb3de19af209c73a551b
3
+ metadata.gz: 6c1fb894c8cc2a6c5f8978956a1d3ede6103cc06
4
+ data.tar.gz: 2aae2171c1243176dbeb2792d916088aaca9346c
5
5
  SHA512:
6
- metadata.gz: f62dda1d8c8ef67640109fc358f4aa5d2b0924b1f59b7fef3e572412999224546f1a2b08ca3cfa1eef590747df241fad2239d1fb83d797f00cb1e949d9f8f80f
7
- data.tar.gz: e756632cbc6fd58e1cc78cafca106a1180a674e398919ed90b6a0f921fe2cb0ca874e1b7e8bb37e3f780456d2f2270ee5001d3a5ee51976e1d23b3d4f1469878
6
+ metadata.gz: 7ccdb93391b1c71fab8eb744a242fd5034759f71522424cc620e3ea4bc409efb26a29a0bb1191c5182dcf65da8fda8142c5c8067aee802ed0fc3bd5b360e0fe1
7
+ data.tar.gz: 97f49c730d0387c4cf30bc0f1274917240fec2c4d204fef28c342a7c40454641c86bbb6ee659c699d200d474ecf6f94707f2f153372af3acfdda4bfdca0a513d
data/.gitignore CHANGED
@@ -1,7 +1,6 @@
1
1
  .env
2
2
  .bundle
3
3
  *.gem
4
- Gemfile.lock
5
4
  /lib/squad_goals/public/vendor/jquery
6
5
  /lib/squad_goals/public/vendor/bootstrap/grunt
7
6
  /lib/squad_goals/public/vendor/bootstrap/js
@@ -0,0 +1,116 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ squad_goals (0.1.0)
5
+ dalli (~> 2.7)
6
+ dotenv (~> 2.0)
7
+ octokit (~> 4.0)
8
+ rack-ssl-enforcer (~> 0.2)
9
+ sinatra_auth_github (~> 1.1)
10
+ warden-github (~> 1.1)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activesupport (4.2.6)
16
+ i18n (~> 0.7)
17
+ json (~> 1.7, >= 1.7.7)
18
+ minitest (~> 5.1)
19
+ thread_safe (~> 0.3, >= 0.3.4)
20
+ tzinfo (~> 1.1)
21
+ addressable (2.3.8)
22
+ ast (2.2.0)
23
+ coderay (1.1.1)
24
+ crack (0.4.3)
25
+ safe_yaml (~> 1.0.0)
26
+ dalli (2.7.6)
27
+ diff-lcs (1.2.5)
28
+ dotenv (2.1.0)
29
+ faraday (0.9.2)
30
+ multipart-post (>= 1.2, < 3)
31
+ hashdiff (0.3.0)
32
+ i18n (0.7.0)
33
+ json (1.8.3)
34
+ method_source (0.8.2)
35
+ minitest (5.8.4)
36
+ multipart-post (2.0.0)
37
+ octokit (4.3.0)
38
+ sawyer (~> 0.7.0, >= 0.5.3)
39
+ parser (2.3.0.6)
40
+ ast (~> 2.2)
41
+ powerpack (0.1.1)
42
+ pry (0.10.3)
43
+ coderay (~> 1.1.0)
44
+ method_source (~> 0.8.1)
45
+ slop (~> 3.4)
46
+ rack (1.6.4)
47
+ rack-protection (1.5.3)
48
+ rack
49
+ rack-ssl-enforcer (0.2.9)
50
+ rack-test (0.6.3)
51
+ rack (>= 1.0)
52
+ rainbow (2.1.0)
53
+ rake (10.5.0)
54
+ rspec (3.4.0)
55
+ rspec-core (~> 3.4.0)
56
+ rspec-expectations (~> 3.4.0)
57
+ rspec-mocks (~> 3.4.0)
58
+ rspec-core (3.4.3)
59
+ rspec-support (~> 3.4.0)
60
+ rspec-expectations (3.4.0)
61
+ diff-lcs (>= 1.2.0, < 2.0)
62
+ rspec-support (~> 3.4.0)
63
+ rspec-mocks (3.4.1)
64
+ diff-lcs (>= 1.2.0, < 2.0)
65
+ rspec-support (~> 3.4.0)
66
+ rspec-support (3.4.1)
67
+ rubocop (0.37.2)
68
+ parser (>= 2.3.0.4, < 3.0)
69
+ powerpack (~> 0.1)
70
+ rainbow (>= 1.99.1, < 3.0)
71
+ ruby-progressbar (~> 1.7)
72
+ unicode-display_width (~> 0.3)
73
+ ruby-progressbar (1.7.5)
74
+ safe_yaml (1.0.4)
75
+ sawyer (0.7.0)
76
+ addressable (>= 2.3.5, < 2.5)
77
+ faraday (~> 0.8, < 0.10)
78
+ sinatra (1.4.7)
79
+ rack (~> 1.5)
80
+ rack-protection (~> 1.4)
81
+ tilt (>= 1.3, < 3)
82
+ sinatra_auth_github (1.2.0)
83
+ sinatra (~> 1.0)
84
+ warden-github (~> 1.2.0)
85
+ slop (3.6.0)
86
+ thread_safe (0.3.5)
87
+ tilt (2.0.2)
88
+ tzinfo (1.2.2)
89
+ thread_safe (~> 0.1)
90
+ unicode-display_width (0.3.1)
91
+ warden (1.2.6)
92
+ rack (>= 1.0)
93
+ warden-github (1.2.0)
94
+ activesupport (> 3.0)
95
+ octokit (> 2.1.0)
96
+ warden (> 1.0)
97
+ webmock (1.24.1)
98
+ addressable (>= 2.3.6)
99
+ crack (>= 0.3.2)
100
+ hashdiff
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ bundler (~> 1.11)
107
+ pry (~> 0.10)
108
+ rack-test (~> 0.6)
109
+ rake (~> 10.0)
110
+ rspec (~> 3.1)
111
+ rubocop (~> 0.37)
112
+ squad_goals!
113
+ webmock (~> 1.2)
114
+
115
+ BUNDLED WITH
116
+ 1.11.2
data/README.md CHANGED
@@ -51,6 +51,10 @@ The following environmental values should be set:
51
51
  * `GITHUB_TOKEN` - A personal access token for a user with admin rights to the organization
52
52
  * `GITHUB_TEAMS` - Comma-separated list of team names you'd like to allow access to, e.g, `red-team,blue-team`
53
53
 
54
+ ### Memcache
55
+
56
+ You'll also need an instance of Memecache running, to cache API calls. If using Herkou, this can be within [memcachier's free use tier](https://devcenter.heroku.com/articles/memcachier).
57
+
54
58
  ### Customizing Views
55
59
 
56
60
  There are three views, `success`, `forbidden`, and `error`. They're pretty boring by default, so you may want to swap them out for something a bit my snazzy. If you had a views directory along side your `config.ru`, you can do so like this in your `config.ru` file:
@@ -3,39 +3,31 @@ require 'octokit'
3
3
  require 'dotenv'
4
4
  require 'yaml'
5
5
  require 'tilt/erb'
6
-
6
+ require 'dalli'
7
+ require 'digest'
7
8
  require 'squad_goals/version'
8
- require 'squad_goals/helpers'
9
- require 'squad_goals/app'
10
9
 
11
10
  module SquadGoals
12
- def self.root
13
- File.expand_path './squad_goals', File.dirname(__FILE__)
14
- end
11
+ autoload :Helpers, 'squad_goals/helpers'
12
+ autoload :App, 'squad_goals/app'
13
+ autoload :Team, 'squad_goals/team'
14
+ autoload :Organization, 'squad_goals/organization'
15
15
 
16
- def self.views_dir
17
- @views_dir ||= File.expand_path 'views', SquadGoals.root
18
- end
16
+ class << self
17
+ def root
18
+ File.expand_path './squad_goals', File.dirname(__FILE__)
19
+ end
19
20
 
20
- def self.views_dir=(dir)
21
- @views_dir = dir
22
- end
21
+ def views_dir
22
+ @views_dir ||= File.expand_path 'views', SquadGoals.root
23
+ end
23
24
 
24
- def self.public_dir
25
- @public_dir ||= File.expand_path 'public', SquadGoals.root
26
- end
25
+ attr_writer :views_dir
27
26
 
28
- def self.public_dir=(dir)
29
- @public_dir = dir
30
- end
31
- end
27
+ def public_dir
28
+ @public_dir ||= File.expand_path 'public', SquadGoals.root
29
+ end
32
30
 
33
- unless SquadGoals::App.production?
34
- Dotenv.load
35
- stack = Faraday::RackBuilder.new do |builder|
36
- builder.response :logger
37
- builder.use Octokit::Response::RaiseError
38
- builder.adapter Faraday.default_adapter
31
+ attr_writer :public_dir
39
32
  end
40
- Octokit.middleware = stack
41
33
  end
@@ -1,9 +1,9 @@
1
1
  module SquadGoals
2
2
  class App < Sinatra::Base
3
- include SquadGoals::Helpers
4
-
5
3
  set :github_options, scopes: ''
6
4
 
5
+ MEMCACHE_TTL = 60 * 5 # TTL for caching team info, in seconds
6
+
7
7
  use Rack::Session::Cookie, http_only: true,
8
8
  secret: ENV['SESSION_SECRET'] || SecureRandom.hex
9
9
 
@@ -20,20 +20,61 @@ module SquadGoals
20
20
  use Rack::SslEnforcer
21
21
  end
22
22
 
23
+ configure :development do
24
+ Dotenv.load
25
+ Octokit.middleware = Faraday::RackBuilder.new do |builder|
26
+ builder.response :logger
27
+ builder.use Octokit::Response::RaiseError
28
+ builder.adapter Faraday.default_adapter
29
+ end
30
+ end
31
+
32
+ configure do
33
+ set :organization, Organization.new(ENV['GITHUB_ORG_ID'])
34
+ set :client, Octokit::Client.new(access_token: ENV['GITHUB_TOKEN'])
35
+ set :dalli, Dalli::Client.new((ENV['MEMCACHIER_SERVERS'] || 'localhost:11211').split(','),
36
+ username: ENV['MEMCACHIER_USERNAME'],
37
+ password: ENV['MEMCACHIER_PASSWORD'],
38
+ failover: true,
39
+ socket_timeout: 1.5,
40
+ socket_failure_delay: 0.2,
41
+ exires_in: MEMCACHE_TTL)
42
+ end
43
+
44
+ def team_requested
45
+ settings.organization.teams.find { |team| team.slug == params['team'] }
46
+ end
47
+
48
+ helpers do
49
+ def avatar(user)
50
+ "<img class=\"avatar avatar-small\" src=\"https://github.com/#{user}.png\" alt=\"user\" width=\"20\" height=\"20\">"
51
+ end
52
+ end
53
+
54
+ def locals
55
+ {
56
+ organization: settings.organization,
57
+ teams: settings.organization.teams,
58
+ org_id: settings.organization.login,
59
+ team_requested: team_requested,
60
+ user: (github_user.login if github_user)
61
+ }
62
+ end
63
+
23
64
  get '/' do
24
- erb :index, locals: { teams: teams, org_id: org_id }
65
+ erb :index, locals: locals
25
66
  end
26
67
 
27
68
  get '/join/:team' do
28
- if team.nil? # invalid team
69
+ if team_requested.nil? # invalid team
29
70
  status 409
30
71
  halt erb :error
31
72
  end
32
73
 
33
74
  authenticate!
34
- add!
75
+ team_requested.add(github_user.login)
35
76
 
36
- erb :success, locals: { team: team, org_id: org_id }
77
+ erb :success, locals: locals
37
78
  end
38
79
  end
39
80
  end
@@ -1,33 +1,27 @@
1
1
  module SquadGoals
2
2
  module Helpers
3
-
4
- def teams
5
- @teams ||= begin
6
- teams = client.organization_teams(org_id)
7
- teams.select { |team| team_whitelist.include? team.slug }
8
- end
3
+ # Call octokit, using memcached response, when available
4
+ def client_call(method, *args)
5
+ key = cache_key(method, args)
6
+ cached = dalli.get(key)
7
+ return cached if cached
8
+ response = client.send(method, *args)
9
+ dalli.set(key, response)
10
+ response
9
11
  end
10
12
 
11
13
  private
12
14
 
13
- def team_whitelist
14
- ENV['GITHUB_TEAMS'].split(",")
15
+ def cache_key(method, *args)
16
+ Digest::SHA1.hexdigest(method.to_s + ': ' + args.join(', '))
15
17
  end
16
18
 
17
19
  def client
18
- @client ||= Octokit::Client.new access_token: ENV['GITHUB_TOKEN']
19
- end
20
-
21
- def org_id
22
- ENV['GITHUB_ORG_ID']
23
- end
24
-
25
- def team
26
- teams.find { |team| team.slug == params["team"] }
20
+ SquadGoals::App.client
27
21
  end
28
22
 
29
- def add!
30
- client.add_team_membership team.id, github_user.login
23
+ def dalli
24
+ SquadGoals::App.dalli
31
25
  end
32
26
  end
33
27
  end
@@ -0,0 +1,31 @@
1
+ module SquadGoals
2
+ class Organization
3
+ include SquadGoals::Helpers
4
+
5
+ attr_reader :login
6
+
7
+ def initialize(login)
8
+ @login = login.downcase.strip
9
+ end
10
+
11
+ def name
12
+ meta.name || "@#{meta.login}"
13
+ end
14
+
15
+ def teams
16
+ client_call(:organization_teams, login).map do |team|
17
+ Team.new(team)
18
+ end.select(&:whitelisted?)
19
+ end
20
+
21
+ def member?(user)
22
+ client_call :organization_member?, login, user
23
+ end
24
+
25
+ private
26
+
27
+ def meta
28
+ @meta ||= client_call :organization, login
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module SquadGoals
2
+ class Team
3
+ include SquadGoals::Helpers
4
+
5
+ attr_reader :id, :name, :slug, :description
6
+
7
+ def initialize(hash)
8
+ @id = hash[:id]
9
+ @name = hash[:name]
10
+ @slug = hash[:slug]
11
+ @description = hash[:description]
12
+ end
13
+
14
+ def whitelisted?
15
+ Team.whitelist.include?(slug)
16
+ end
17
+
18
+ def members
19
+ client_call :team_members, id
20
+ end
21
+
22
+ def member?(user)
23
+ user && members.map { |u| u.login.downcase }.include?(user.downcase)
24
+ end
25
+
26
+ def add(user)
27
+ response = client_call :add_team_membership, id, user
28
+ dalli.flush
29
+ response
30
+ end
31
+
32
+ class << self
33
+ def whitelist
34
+ @whitelist ||= ENV['GITHUB_TEAMS'].split(',').map { |t| t.downcase.strip }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module SquadGoals
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,8 +1,30 @@
1
- <h1></h1>
2
-
3
- <h2>@<%= org_id %>'s Teams</h2>
1
+ <h2><%= organization.name %>'s Teams</h2>
4
2
  <% teams.each do |team| %>
5
3
  <h3><%= team.name %></h3>
6
4
  <p><%= team.description %></p>
7
- <p><a class="btn" href="/join/<%= team.slug %>">Join @<%= org_id %>'s <%= team.name %></a></p>
5
+ <p>
6
+ <% if team.member?(user) %>
7
+ You're already a member of
8
+ <% else %>
9
+ <a class="btn" href="/join/<%= team.slug %>">Join
10
+ <% members = team.members.shuffle %>
11
+ <% if members.count != 0 %>
12
+ <% if members.count == 1 %>
13
+ <%= avatar members.first.login %>
14
+ <% elsif members.count == 2 %>
15
+ <%= avatar members[0].login %> and
16
+ <%= avatar members[1].login %>
17
+ <% elsif members.count == 3 %>
18
+ <%= avatar members[0].login %>,
19
+ <%= avatar members[1].login %>, and
20
+ <%= avatar members[2].login %>,
21
+ <% else %>
22
+ <%= avatar members[0].login %>,
23
+ <%= avatar members[1].login %>,
24
+ and <% members.count - 2 %> others
25
+ <% end %>
26
+ on
27
+ <% end %>
28
+ <% end %>
29
+ @<%= org_id %>'s <%= team.name %></a></p>
8
30
  <% end %>
@@ -1,9 +1,16 @@
1
1
  <h2>Success!</h2>
2
2
 
3
- <p>
4
- You've successsfully joined @<%= org_id %>'s '<%= team.name %>.
5
- </p>
3
+ <% if organization.member?(user) %>
4
+ <p>
5
+ You've successsfully joined <%= organization.name %>'s '<%= team_requested.name %>.
6
+ </p>
6
7
 
7
- <p>
8
- <a href="/">Join another team?</a>
9
- </p>
8
+ <p>
9
+ <a href="/">Join another team?</a>
10
+ </p>
11
+ <% else %>
12
+ <p>
13
+ Almost there. Just one more step.
14
+ Please <a href="https://github.com/orgs/<%= organization.login %>/invitation">accept this invitation</a> to join <%= organization.name %>, so that we can add you to the <%= team_requested.name %> team.
15
+ </p>
16
+ <% end %>
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ['ben.balter@github.com']
11
11
 
12
12
  spec.summary = 'A tiny app to allow open-source contributors to opt-in to GitHub teams.'
13
- spec.homepage = "https://github.com/benbalter/squad_goals"
13
+ spec.homepage = 'https://github.com/benbalter/squad_goals'
14
14
  spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.add_dependency 'octokit', '~> 4.0'
24
24
  spec.add_dependency 'rack-ssl-enforcer', '~> 0.2'
25
25
  spec.add_dependency 'dotenv', '~> 2.0'
26
+ spec.add_dependency 'dalli', '~> 2.7'
27
+
26
28
  spec.add_development_dependency 'rspec', '~> 3.1'
27
29
  spec.add_development_dependency 'rack-test', '~> 0.6'
28
30
  spec.add_development_dependency 'webmock', '~> 1.2 '
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squad_goals
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Balter
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-09 00:00:00.000000000 Z
11
+ date: 2016-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: warden-github
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dalli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.7'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.7'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rspec
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -190,6 +204,7 @@ files:
190
204
  - ".rspec"
191
205
  - ".travis.yml"
192
206
  - Gemfile
207
+ - Gemfile.lock
193
208
  - LICENSE.txt
194
209
  - README.md
195
210
  - Rakefile
@@ -200,6 +215,7 @@ files:
200
215
  - lib/squad_goals.rb
201
216
  - lib/squad_goals/app.rb
202
217
  - lib/squad_goals/helpers.rb
218
+ - lib/squad_goals/organization.rb
203
219
  - lib/squad_goals/public/vendor/bootstrap/.bower.json
204
220
  - lib/squad_goals/public/vendor/bootstrap/LICENSE
205
221
  - lib/squad_goals/public/vendor/bootstrap/bower.json
@@ -225,6 +241,7 @@ files:
225
241
  - lib/squad_goals/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff
226
242
  - lib/squad_goals/public/vendor/bootstrap/fonts/glyphicons-halflings-regular.woff2
227
243
  - lib/squad_goals/public/vendor/bootstrap/package.json
244
+ - lib/squad_goals/team.rb
228
245
  - lib/squad_goals/version.rb
229
246
  - lib/squad_goals/views/error.erb
230
247
  - lib/squad_goals/views/index.erb
@@ -254,8 +271,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
271
  version: '0'
255
272
  requirements: []
256
273
  rubyforge_project:
257
- rubygems_version: 2.5.1
274
+ rubygems_version: 2.6.2
258
275
  signing_key:
259
276
  specification_version: 4
260
277
  summary: A tiny app to allow open-source contributors to opt-in to GitHub teams.
261
278
  test_files: []
279
+ has_rdoc: