squad_goals 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: