aweplug 1.0.0.a2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+ Gemfile.lock
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+ .bundle
21
+ vendor/bundle
22
+ vendor/cache
23
+ pkg
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --fail-fast
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 LightGuard
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.adoc ADDED
@@ -0,0 +1,3 @@
1
+ == aweplug
2
+
3
+ A set of Awestruct extensions aimed at creating OSS project websites.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Aweplug
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'aweplug'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install aweplug
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'aweplug/version'
4
+ require 'guard'
5
+
6
+ GEMFILE = "aweplug-#{Aweplug::VERSION}.gem"
7
+
8
+ task :default => :build
9
+
10
+ desc "Run all tests and build the gem"
11
+ task :build => 'test:spec' do
12
+ system "gem build aweplug.gemspec"
13
+ end
14
+
15
+ desc "Build and install the gem locally"
16
+ task :install => :build do
17
+ system "gem install -l -f #{GEMFILE}"
18
+ end
19
+
20
+ namespace :release do
21
+ desc "Release the gem to rubygems"
22
+ task :push => [ :build, :tag ] do
23
+ system "gem push #{GEMFILE}"
24
+ end
25
+
26
+ desc "Create tag #{Aweplug::VERSION} in git"
27
+ task :tag do
28
+ system "git tag #{Aweplug::VERSION}"
29
+ end
30
+ end
31
+
32
+ namespace :test do
33
+ if !defined?(RSpec)
34
+ puts "spec targets require RSpec"
35
+ else
36
+ desc "Run all specifications"
37
+ RSpec::Core::RakeTask.new(:spec) do |t|
38
+ t.pattern = 'spec/**/*_spec.rb'
39
+ end
40
+ end
41
+
42
+ desc "Start Guard to listen for changes and run specs"
43
+ task :guard do
44
+ Guard.start(:guardfile => 'Guardfile')
45
+ Guard.run_all
46
+ while ::Guard.running do
47
+ sleep 0.5
48
+ end
49
+ end
50
+ end
51
+
data/aweplug.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aweplug/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "aweplug"
8
+ gem.version = Aweplug::VERSION
9
+ gem.authors = ["LightGuard"]
10
+ gem.email = ["lightguard.jp@gmail.com"]
11
+ gem.description = %q{A set of Awestruct extensions for building a project website}
12
+ gem.summary = %q{This set of Awestruct extensions includes helpful tools,
13
+ extensions and helpers for building a website using Awestruct.
14
+ It includes extensions for accessing Github, JIRA, managing identities and others.}
15
+ gem.homepage = "https://github.com/awestruct/aweplug"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ #gem.add_dependency 'awestruct', '>= 0.5.1'
23
+ gem.add_dependency 'octokit', '>= 1.24.0'
24
+ gem.add_dependency 'faraday', '>= 0.8.7'
25
+ gem.add_dependency 'faraday_middleware', '>= 0.9.0'
26
+
27
+ gem.add_development_dependency 'guard-rspec', '~> 3.0.0'
28
+ gem.add_development_dependency 'rake', '~> 10.0.4'
29
+ end
@@ -0,0 +1,187 @@
1
+ require 'json'
2
+ require 'aweplug/extensions/restclient_extensions'
3
+ require 'aweplug/extensions/identity/github'
4
+ require 'aweplug/extensions/identity/gravatar'
5
+ require 'aweplug/extensions/identity/confluence'
6
+ require 'aweplug/extensions/identity/jbosscommunity'
7
+
8
+ module Aweplug
9
+ module Extensions
10
+ module Identities
11
+ # QUESTION should we call this Initializer or Loader instead?
12
+ class Storage
13
+ def initialize(opts = {})
14
+ @use_data_cache = opts[:use_data_cache] || true
15
+ end
16
+
17
+ # Internal: Awestruct calls this method during site generation.
18
+ # Iterates through all the different identity in the site and finds
19
+ # information about them.
20
+ #
21
+ # site - This must be the site object provided by awestruct
22
+ #
23
+ # Returns nothing
24
+ def execute(site)
25
+ data_file = File.join(site.tmp_dir, 'datacache', 'identity.yml')
26
+ loaded = false
27
+ identities = []
28
+ if @use_data_cache and File.exist? data_file
29
+ identities = YAML.load_file data_file
30
+ loaded = true
31
+ else
32
+ if site.identities
33
+ identities = site.identities.map { |i| OpenStruct.new i }
34
+ end
35
+ end
36
+ identities.extend(Locators)
37
+ identities.loaded = loaded
38
+ site.identities = identities
39
+ end
40
+
41
+ module Locators
42
+ attr_accessor :loaded
43
+
44
+ def lookup(username, create = false)
45
+ identity = self.find { |e| username.eql? e.username } ||
46
+ self.find { |e| username.eql? e.jboss_username }
47
+ if create and identity.nil?
48
+ identity = OpenStruct.new({:username => username})
49
+ if block_given?
50
+ yield identity
51
+ end
52
+ self << identity
53
+ end
54
+ identity
55
+ end
56
+
57
+ def lookup_by_name(name, create = false)
58
+ search = name.downcase
59
+ identity = self.find { |e| search.eql? e.name.to_s.downcase }
60
+ if create and identity.nil?
61
+ identity = OpenStruct.new({:name => name})
62
+ if block_given?
63
+ yield identity
64
+ end
65
+ self << identity
66
+ end
67
+ identity
68
+ end
69
+
70
+ def lookup_by_contributor(contributor)
71
+ identity = self.find { |e| e.contributor and e.contributor.emails and e.contributor.emails.include? contributor.email }
72
+ if identity.nil?
73
+ # Indication that we have a mismatched account
74
+ puts "Could not find contributor with e-mail " + contributor.email
75
+ end
76
+ identity
77
+ end
78
+
79
+ def lookup_by_email(email, create = false)
80
+ identity = self.find { |e| email.eql? e.email or !e.emails.nil? and e.emails.include? email }
81
+ if create and identity.nil?
82
+ identity = OpenStruct.new({:email => email})
83
+ if block_given?
84
+ yield identity
85
+ end
86
+ self << identity
87
+ end
88
+ identity
89
+ end
90
+
91
+ def lookup_by_github_id(github_id, create = false)
92
+ identity = self.find { |e| github_id.eql? e.github_id }
93
+ if create and identity.nil?
94
+ identity = OpenStruct.new({:github_id => github_id})
95
+ if block_given?
96
+ yield identity
97
+ end
98
+ self << identity
99
+ end
100
+ identity
101
+ end
102
+
103
+ def lookup_by_twitter_username(username)
104
+ self.find { |e| !e.twitter.nil? and username.eql? e.twitter.username }
105
+ end
106
+
107
+ def locate(*query)
108
+ query.map! { |e| e.downcase }
109
+ self.find { |e| not ([e.username, e.name.to_s.downcase, e.email] & query).empty? }
110
+ end
111
+
112
+ def core_team()
113
+ self.select { |e| e.core }
114
+ end
115
+
116
+ def contributors()
117
+ self.select { |e| e.contributor }
118
+ end
119
+
120
+ def speakers()
121
+ self.select { |e| e.speaker }
122
+ end
123
+
124
+ def translators()
125
+ self.select { |e| e.translator }
126
+ end
127
+ end
128
+ end
129
+
130
+ module IdentityHelper
131
+ # SEMI-HACK this should received contributions from crawlers
132
+ def url
133
+ self.jbosscommunity ? self.jbosscommunity.profile_url : self.github.profile_url
134
+ end
135
+
136
+ # TODO support for CSS class, or just attributes in general
137
+ def to_link
138
+ "<a href=\"#{self.url}\">#{self.name}</a>"
139
+ end
140
+
141
+ def to_s
142
+ self.name
143
+ end
144
+ end
145
+
146
+ class Collect
147
+ def initialize(*collectors)
148
+ @collectors = collectors
149
+ end
150
+
151
+ def execute(site)
152
+ if !site.identities.loaded
153
+ @collectors.each do |c|
154
+ c.collect(site.identities)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ class Crawl
161
+ def initialize(*crawlers)
162
+ @crawlers = crawlers
163
+ end
164
+
165
+ def execute(site)
166
+ site.identities.each do |i|
167
+ @crawlers.each do |c|
168
+ c.crawl(i) if !site.identities.loaded
169
+ i.extend(IdentityHelper)
170
+ c.enhance(i) if c.respond_to? 'enhance'
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ class Cache
177
+ def execute(site)
178
+ data_file = File.join(site.tmp_dir, 'datacache', 'identity.yml')
179
+ FileUtils.mkdir_p File.dirname data_file
180
+ File.open(data_file, 'w') do |out|
181
+ YAML.dump site.identities, out
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,107 @@
1
+ require 'pathname'
2
+
3
+ module Aweplug
4
+ module Extensions
5
+ module Identity
6
+ module Confluence
7
+ class Crawler
8
+ SESSION_PATH = '/rest/prototype/1/session'
9
+ SEARCH_PATH = '/rest/prototype/1/search/user'
10
+ SEARCH_PARAM = 'query'
11
+
12
+ # Public: Initialize a Crawler
13
+ #
14
+ # base_url - The String of the base confluence url, no trailing slash
15
+ # opts[:assign_to] - The String OpenStruct keys to send the data
16
+ # opts[:identity_search_keys] - The String OpenStruct keys to use to locate the search query
17
+ # opts[:assign_username_to] - The String OpenStruct key to which to assign the username
18
+ # opts[:auth_file] - The File containing the authentication credentials for secure requests
19
+ def initialize(base_url, opts = {})
20
+ @base_url = base_url
21
+ @identity_search_keys = opts[:identity_search_keys] || ['name']
22
+ @assign_to = opts[:assign_to] || 'confluence'
23
+ @assign_username_to = opts[:assign_username_to]
24
+ @auth_file = opts[:auth_file]
25
+ @session_cookie = nil
26
+ end
27
+
28
+ def crawl(identity)
29
+ search = nil
30
+ queries = []
31
+ data = nil
32
+ @identity_search_keys.each do |k|
33
+ search = identity.send(k)
34
+ next if search.nil?
35
+ # lowercase and remove middle names (shouldn't affect usernames)
36
+ search = search.downcase.gsub(/^([^ ]+ ?).*?([^ ]+)$/, '\1\2')
37
+ # don't search on same string twice
38
+ next if queries.include? search
39
+ queries << search
40
+ cleansed_query = search.gsub(' ', '-')
41
+ url = File.join(@base_url, SEARCH_PATH) + '?query=' + URI.encode(search)
42
+ data = RestClient.get(url, :cache_key => "confluence/query-#{cleansed_query}.json", :accept => 'application/json')
43
+ if data['totalSize'].to_i == 0
44
+ #puts "No results, advancing confluence user crawler to next query string for #{search}"
45
+ data = nil
46
+ #elsif data['totalSize'] > 1
47
+ # puts "Too many results, skipping confluence user crawler for #{search}."
48
+ # data = nil
49
+ else
50
+ break
51
+ end
52
+ end
53
+
54
+ if data.nil?
55
+ if queries.empty?
56
+ puts "No property found to search, skipping confluence user crawler for #{identity.name}"
57
+ else
58
+ puts "No results, skipping confluence user crawler for #{queries.join ' / '}"
59
+ end
60
+ return
61
+ end
62
+
63
+ user = data['result'].first
64
+ username = user['username']
65
+ url = user['link'].select{|l| l['href'].end_with? "~#{username}" }.first['href']
66
+ identity.send(@assign_to + '=', OpenStruct.new({:username => username, :profile_url => url}))
67
+ if !@assign_username_to.nil?
68
+ identity.send(@assign_username_to + '=', username)
69
+ end
70
+
71
+ get_session_cookie()
72
+ if @session_cookie
73
+ profile = RestClient.get(url, :cache_key => "confluence/user-#{username}.html", :cookie => @session_cookie)
74
+ if profile.match(/ id="email".*?>(.+?)</)
75
+ replace = {' dot ' => '.', ' at ' => '@'}
76
+ email = $1.gsub(/ (dot|at) /) {|m| replace[m]}
77
+ identity.email = email if identity.email.nil?
78
+ identity.emails ||= []
79
+ identity.emails |= [identity.email, email]
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ def get_session_cookie()
86
+ if @session_cookie.nil?
87
+ @session_cookie = false
88
+ credentials = nil
89
+ if !@auth_file.nil?
90
+ if File.exist? @auth_file
91
+ credentials = File.read(@auth_file)
92
+ elsif Pathname.new(@auth_file).relative? and File.exist? File.join(ENV['HOME'], @auth_file)
93
+ credentials = File.read(File.join(ENV['HOME'], @auth_file))
94
+ end
95
+ end
96
+ if !credentials.nil?
97
+ url = @base_url.gsub(/^(https?:\/\/)/, '\1' + credentials.chomp + '@') + SESSION_PATH
98
+ response = RestClient.head(url)
99
+ @session_cookie = response.headers[:set_cookie].first.split(';').first
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,230 @@
1
+ module Aweplug
2
+ module Extensions
3
+ module Identity
4
+ module GitHub
5
+ CONTRIBUTORS_URL_TEMPLATE = 'https://api.github.com/repos/%s/%s/contributors'
6
+ TEAM_MEMBERS_URL_TEMPLATE = 'https://api.github.com/teams/%s/members'
7
+ USER_URL_TEMPLATE = 'https://api.github.com/users/%s'
8
+
9
+ # It appears that all github users have a gravatar_id
10
+ AVATAR_URL_TEMPLATE = 'http://gravatar.com/avatar/%s?s=%i'
11
+ # TODO make the default avatar configurable
12
+ FALLBACK_AVATAR_URL_TEMPLATE = 'https://community.jboss.org/people/sbs-default-avatar/avatar/%i.png'
13
+ module IdentityHelper
14
+ def avatar_url(size = 48)
15
+ if !self.gravatar_id.nil?
16
+ AVATAR_URL_TEMPLATE % [self.gravatar_id, size]
17
+ else
18
+ FALLBACK_URL_TEMPLATE % size
19
+ end
20
+ end
21
+ end
22
+
23
+ class Collector
24
+
25
+ def initialize(opts = {})
26
+ @repositories = []
27
+ @match_filters = []
28
+ @teams = opts[:teams]
29
+ @auth_file = opts[:auth_file]
30
+ @credentials = nil
31
+ end
32
+
33
+ def add_repository(repository)
34
+ @repositories << repository
35
+ end
36
+
37
+ def add_match_filter(match_filter)
38
+ @match_filters << match_filter
39
+ #File.open('/tmp/committers.yml', 'w') do |out|
40
+ # YAML.dump(match_filter, out)
41
+ #end
42
+ end
43
+
44
+ def collect(identities)
45
+ visited = []
46
+ @repositories.each do |r|
47
+ url = CONTRIBUTORS_URL_TEMPLATE % [r.owner, r.path]
48
+ contributors = RestClient.get url, :accept => 'application/json'
49
+ contributors.each do |acct|
50
+ github_id = acct['login'].downcase
51
+ author = nil
52
+ @match_filters.each do |filter|
53
+ author = filter.values.find { |candidate| candidate.github_id.eql? github_id }
54
+ break unless author.nil?
55
+ end
56
+ if author.nil?
57
+ #puts "Skipping non-Arquillian contributor #{github_id} in repository #{r.owner}/#{r.path}"
58
+ next
59
+ end
60
+ identity = identities.lookup_by_github_id(github_id, true)
61
+ github_acct_to_identity(acct, author, identity)
62
+ visited << github_id
63
+ end
64
+ end
65
+
66
+ # github doesn't keep perfect records of contributors, so handle those omitted contributors
67
+ @match_filters.each do |filter|
68
+ filter.values.select { |author| !author.github_id.nil? and !visited.include? author.github_id }.each do |author|
69
+ github_id = author.github_id
70
+ puts "Manually adding #{author.name} (#{github_id}) as a contributor"
71
+ url = USER_URL_TEMPLATE % [github_id]
72
+ user = RestClient.get url, :accept => 'application/json'
73
+ identity = identities.lookup_by_github_id(github_id, true)
74
+ github_acct_to_identity(user, author, identity)
75
+ end
76
+ end
77
+
78
+ if !@teams.nil?
79
+ get_credentials()
80
+ if @credentials
81
+ @teams.each do |team|
82
+ url = TEAM_MEMBERS_URL_TEMPLATE % team[:id]
83
+ url = url.gsub(/^(https?:\/\/)/, '\1' + @credentials.chomp + '@')
84
+ members = RestClient.get(url, :accept => 'application/json')
85
+ members.each do |m|
86
+ github_id = m['login']
87
+ identity = identities.lookup_by_github_id(github_id)
88
+ # identity should not be null, mostly for testing
89
+ if !identity.nil?
90
+ identity.send(team[:name] + '=', true)
91
+ identity.teams = [] if identity.teams.nil?
92
+ identity.teams << team[:name]
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def github_acct_to_identity(acct, author, identity)
101
+ # setup if first visit
102
+ if identity.github.nil? or identity.github.contributions.nil?
103
+ identity.github = OpenStruct.new if identity.github.nil?
104
+ identity.github.contributions = acct.has_key?('contributions') ? acct['contributions'] : 0
105
+ identity.github_url = acct['url'].downcase
106
+ identity.gravatar_id = acct['gravatar_id']
107
+ # author contains the commits we counted from analyzing the repositories
108
+ identity.contributor = author
109
+ identity.email = author.emails.first if identity.email.nil?
110
+ identity.emails ||= []
111
+ identity.emails |= [identity.email, author.emails.first]
112
+ # alias for convenience
113
+ identity.commits = author.commits
114
+ # assume they want their commit name as the preferred name (unless it's an email)
115
+ if identity.name.nil? and author.name.index('@').nil?
116
+ if !author.name.index(' ').nil?
117
+ # FIXME this could be made into a smarter utility function
118
+ identity.name = author.name.split(' ').map { |n| n.capitalize }.join(' ')
119
+ # special exception for Lincoln :)
120
+ identity.name.gsub!('Iii', 'III')
121
+ else
122
+ identity.name = author.name.capitalize
123
+ end
124
+ end
125
+ # if been there, just add contributions according to github
126
+ else
127
+ identity.github.contributions += acct.has_key?('contributions') ? acct['contributions'] : 0
128
+ end
129
+ end
130
+
131
+ def get_credentials()
132
+ if @credentials.nil?
133
+ @credentials = false
134
+ if !@auth_file.nil?
135
+ if File.exist? @auth_file
136
+ @credentials = File.read(@auth_file)
137
+ elsif Pathname.new(@auth_file).relative? and File.exist? File.join(ENV['HOME'], @auth_file)
138
+ @credentials = File.read(File.join(ENV['HOME'], @auth_file))
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ class Crawler
146
+
147
+ def initialize(opts = {})
148
+ @auth_file = opts[:auth_file]
149
+ @credentials = nil
150
+ end
151
+
152
+ PROFILE_URL_TEMPLATE = 'https://api.github.com/users/%s'
153
+
154
+ def enhance(identity)
155
+ identity.extend(IdentityHelper)
156
+ end
157
+
158
+ def get_credentials()
159
+ if @credentials.nil?
160
+ @credentials = false
161
+ if !@auth_file.nil?
162
+ if File.exist? @auth_file
163
+ @credentials = File.read(@auth_file)
164
+ elsif Pathname.new(@auth_file).relative? and File.exist? File.join(ENV['HOME'], @auth_file)
165
+ @credentials = File.read(File.join(ENV['HOME'], @auth_file))
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ def crawl(identity)
172
+ url = identity.github_url
173
+ if url.nil?
174
+ if !identity.github_id.nil?
175
+ url = PROFILE_URL_TEMPLATE % identity.github_id
176
+ end
177
+ end
178
+
179
+ # can't find user, give up (no github id or explicit url)
180
+ if url.nil?
181
+ return
182
+ end
183
+
184
+ get_credentials()
185
+ url = url.gsub(/^(https?:\/\/)/, '\1' + @credentials.chomp + '@')
186
+ data = RestClient.get url, :accept => 'application/json'
187
+ identity.github_id = data['login'].downcase
188
+ identity.username = identity.github_id
189
+ identity.github = OpenStruct.new if identity.github.nil?
190
+ identity.github.merge!(OpenStruct.new({
191
+ :id => data['id'],
192
+ :created => data['created_at'],
193
+ :username => identity.username,
194
+ :profile_url => data['html_url'].downcase
195
+ }))
196
+ # only overwrite name if it's longer than the one already there
197
+ if !data['name'].nil? and (identity.name.nil? or data['name'].length > identity.name.length)
198
+ identity.name = data['name'].titleize
199
+ end
200
+ identity.emails ||= []
201
+ identity.emails |= [identity.email].compact
202
+ keys_to_identity = ['email', 'gravatar_id', 'blog', 'location', 'bio', 'company']
203
+ # merge keys, overwriting duplicates
204
+ identity.merge!(OpenStruct.new(data.select { |k, v|
205
+ !v.to_s.strip.empty? and keys_to_identity.include? k
206
+ }))
207
+ if identity.email
208
+ identity.email = identity.email.downcase
209
+ end
210
+ # append email if we got a new one
211
+ identity.emails |= [identity.email]
212
+ # fix blog urls missing http:// prefix
213
+ if !identity.blog.nil? and identity.blog !~ /^https?:\/\//
214
+ identity.blog = 'http://' + identity.blog
215
+ end
216
+
217
+ # manually credited commits
218
+ if identity.commits and !identity.contributor
219
+ identity.contributor = OpenStruct.new({
220
+ :commits => identity.commits,
221
+ :emails => [identity.email],
222
+ :name => identity.name
223
+ })
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,117 @@
1
+ require 'digest/md5'
2
+
3
+ module Aweplug
4
+ module Extensions
5
+ module Identity
6
+ module Gravatar
7
+ class Crawler
8
+ API_URL_TEMPLATE = 'http://en.gravatar.com/%s.json'
9
+ LANYRD_PROFILE_URL_TEMPLATE = 'http://lanyrd.com/profile/%s'
10
+
11
+ def enhance(identity)
12
+ #identity.extend(IdentityHelper)
13
+ end
14
+
15
+ def crawl(identity)
16
+ hash = identity.gravatar_id
17
+ if hash.nil?
18
+ hash = Digest::MD5.new().update(identity.email.downcase).hexdigest
19
+ end
20
+ url = API_URL_TEMPLATE % hash
21
+ response = RestClient.get(url, :user_agent => "rest-client") do |rsp, req, res, &blk|
22
+ if rsp.code.eql? 404
23
+ #rsp = RestClient::Response.create('{}', rsp.net_http_res, rsp.args)
24
+ rsp.instance_variable_set(:@code, 200)
25
+ rsp
26
+ else
27
+ rsp.return!(req, res, &blk)
28
+ end
29
+ end
30
+
31
+ data = JSON.parse response
32
+
33
+ if data.empty?
34
+ return
35
+ end
36
+
37
+ entry = data['entry'].first
38
+
39
+ keys_to_gravatar = {
40
+ 'id' => 'id',
41
+ 'hash' => 'hash',
42
+ 'profileUrl' => 'profile_url'
43
+ }
44
+ identity.gravatar = OpenStruct.new(entry.select { |k, v|
45
+ !v.to_s.strip.empty? and keys_to_gravatar.has_key? k
46
+ }.inject({}) { |h, (k, v)| h.store(keys_to_gravatar[k], v); h })
47
+
48
+ keys_to_identity = {
49
+ 'preferredUsername' => 'preferred_username',
50
+ 'displayName' => 'name_cloak',
51
+ 'aboutMe' => 'bio',
52
+ 'currentLocation' => 'location'
53
+ }
54
+ identity.merge!(OpenStruct.new(entry.select { |k, v|
55
+ !v.to_s.strip.empty? and keys_to_identity.has_key? k
56
+ }.inject({}) { |h, (k, v)| h.store(keys_to_identity[k], v); h }), false)
57
+
58
+ # TODO check if we need a merge here
59
+ if entry.has_key? 'name' and !entry['name'].to_s.strip.empty?
60
+ if identity.names.nil?
61
+ identity.names = OpenStruct.new(entry['name'])
62
+ end
63
+ if identity.name.nil?
64
+ identity.name = identity.names.formatted
65
+ end
66
+ end
67
+
68
+ if entry.has_key? 'email' and !entry['email'].to_s.strip.empty?
69
+ email = entry['email'].downcase
70
+ identity.email = email if identity.email.nil?
71
+ identity.emails ||= []
72
+ identity.emails |= [identity.email, email]
73
+ end
74
+
75
+ (entry['accounts'] || []).each do |a|
76
+ astruct = OpenStruct.new(a)
77
+ if identity.send(a['shortname']).nil?
78
+ identity.send(a['shortname'] + '=', astruct)
79
+ else
80
+ identity.send(a['shortname']).merge!(astruct)
81
+ end
82
+ end
83
+
84
+ # grab twitter usernames supplied by _config/identity.yml
85
+ if identity.twitter.nil? and !identity.twitter_username.nil?
86
+ identity.twitter = OpenStruct.new({
87
+ :username => identity.twitter_username,
88
+ :url => 'http://twitter.com/' + identity.twitter_username
89
+ })
90
+ end
91
+
92
+ # QUESTION do we need the speaker flag check?
93
+ if identity.speaker and !identity.twitter.nil? and identity.lanyrd.nil?
94
+ identity.lanyrd = OpenStruct.new({
95
+ :username => identity.twitter.username,
96
+ :profile_url => LANYRD_PROFILE_URL_TEMPLATE % identity.twitter.username
97
+ })
98
+ end
99
+
100
+ # FIXME either map url to profile_url, or change everywhere else
101
+ (entry['urls'] || []).each do |u|
102
+ identity.urls = [] if identity.urls.nil?
103
+ identity.urls << OpenStruct.new(u)
104
+ if identity.blog.to_s.empty? and u['title'] =~ /blog/i
105
+ identity.blog = u['value']
106
+ end
107
+ if identity.homepage.to_s.empty? and u['title'] =~ /personal/i
108
+ identity.homepage = u['value']
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,35 @@
1
+ module Aweplug
2
+ module Extensions
3
+ module Identity
4
+ module JBossCommunity
5
+ class Crawler
6
+ PROFILE_URL_TEMPLATE = 'https://community.jboss.org/people/%s'
7
+
8
+ def crawl(identity)
9
+ if !identity.jboss_username.nil?
10
+ identity.jbosscommunity = OpenStruct.new({
11
+ :username => identity.jboss_username,
12
+ :profile_url => PROFILE_URL_TEMPLATE % identity.jboss_username
13
+ })
14
+ end
15
+
16
+ if identity.jbosscommunity.nil? and !identity.urls.nil?
17
+ identity.urls.each do |u|
18
+ if u.title =~ /jboss community/i
19
+ identity.jbosscommunity = OpenStruct.new({
20
+ :username => u.value.split('/').last,
21
+ :profile_url => u.value
22
+ })
23
+ identity.jboss_username = identity.jbosscommunity.username
24
+ break
25
+ end
26
+ end
27
+ end
28
+ # TODO actually rip data from community.jboss.org
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,41 @@
1
+ require 'pathname'
2
+ require 'kramdown'
3
+ require 'aweplug/helpers/git_commit_metadata'
4
+ require 'aweplug/helpers/kramdown_metadata'
5
+
6
+ module Aweplug
7
+ module Extensions
8
+ module Kramdown
9
+ class Quickstart
10
+ include Aweplug::Helper::Git::Commit::Metadata
11
+
12
+ def initialize repository, layout
13
+ @repo = repository
14
+ @layout = layout
15
+ end
16
+
17
+ def execute site
18
+ Dir["#{@repo}/**/README.md"].each do |file|
19
+ page = add_to_site site, file
20
+
21
+ metadata = extract_metadata(file)
22
+ metadata[:commits] = commit_info @repo, Pathname.new(file)
23
+
24
+ page.send 'metadata=', @metadata
25
+ # TODO: Upload to DCP
26
+ end
27
+ end
28
+
29
+ def extract_metadata(file)
30
+ (Kramdown::Document.new File.readlines(file).join, :input => 'QuickStartParser').root.options[:metadata]
31
+ end
32
+
33
+ def add_to_site(site, file)
34
+ page = site.engine.load_site_page file
35
+ page.layout = @layout
36
+ page
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ require 'asciidoctor'
2
+
3
+ module Awestruct
4
+ module Extensions
5
+ # Public: Parses (AsciiDoc files currently) and pulls out h2 sections
6
+ # (and their contents), setting them as first class page variables.
7
+ #
8
+ # Examples
9
+ #
10
+ # extension Awestruct::Extensions::Sections.new
11
+ class Sections
12
+ # Internal: Looks for all AsciiDoc files and pulls out the sections,
13
+ # adding them to the page variable.
14
+ #
15
+ # site - The awestruct site variable
16
+ def execute site
17
+ site.pages.each do |page|
18
+ if page.content_syntax =~ /^a(sc)?(ii)?(d(oc)?)?$/
19
+ sections = Asciidoctor.load(page.raw_content).sections
20
+ sections.each do |s|
21
+ r = String.new
22
+ s.blocks.each {|b| r << b.render}
23
+ page.send "#{s.id}=", r
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
File without changes
@@ -0,0 +1,23 @@
1
+ require 'open3'
2
+ require 'json'
3
+
4
+ module Aweplug
5
+ module Helper
6
+ module Git
7
+ module Commit
8
+ module Metadata
9
+ # Public: Retrieves commit information from the git repo containing the file.
10
+ #
11
+ # repo_root - The directory (relative to the site base) containing the git repo
12
+ # file_path - Path to the file being processed, relative to the site base
13
+ #
14
+ # Returns an array of commit info as json values
15
+ def commit_info(repo_root, file_path)
16
+ o, _ = Open3.capture2(%Q[git --git-dir=#{repo_root}/.git log --date=iso --format='{"author":"%an","date":"%ai"}' -- #{file_path.to_s.sub(/#{repo_root}\//, '')}])
17
+ o.split("\n").map{ |l| JSON.parse l, :symbolize_names => true }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ require 'kramdown'
2
+ require 'kramdown/parser/kramdown'
3
+
4
+ module Kramdown
5
+ module Parser
6
+ class QuickStartParser < Kramdown::Parser::Kramdown
7
+ def initialize source, options
8
+ super
9
+ @block_parsers.unshift :author_metadata
10
+ @block_parsers.unshift :level_metadata
11
+ @block_parsers.unshift :technologies_metadata
12
+ @block_parsers.unshift :target_product_metadata
13
+ @block_parsers.unshift :source_metadata
14
+ @block_parsers.unshift :summary_metadata
15
+
16
+ @root.options[:metadata] = {:author => '', :level => '',
17
+ :technologies => '', :target_product => '',
18
+ :source => '', :summary => ''}
19
+ end
20
+
21
+ def parse_author_metadata
22
+ @src.pos += @src.matched_size
23
+ @root.options[:metadata][:author] = @src[3]
24
+ end
25
+ define_parser(:author_metadata, /^(Author:)(\s+)(.*?)$/)
26
+
27
+ def parse_level_metadata
28
+ @src.pos += @src.matched_size
29
+ @root.options[:metadata][:level] = @src[3]
30
+ end
31
+ define_parser(:level_metadata, /^(Level:)(\s+)(.*?)$/)
32
+
33
+ def parse_technologies_metadata
34
+ @src.pos += @src.matched_size
35
+ @root.options[:metadata][:technologies] = @src[3]
36
+ end
37
+ define_parser(:technologies_metadata, /^(Technologies:)(\s+)(.*?)$/)
38
+
39
+ def parse_target_product_metadata
40
+ @src.pos += @src.matched_size
41
+ @root.options[:metadata][:target_product] = @src[3]
42
+ end
43
+ define_parser(:target_product_metadata, /^(Target Product:)(\s+)(.*?)$/)
44
+
45
+ def parse_source_metadata
46
+ @src.pos += @src.matched_size
47
+ @root.options[:metadata][:source] = @src[3][1..-2]
48
+ end
49
+ define_parser(:source_metadata, /^(Source:)(\s+)(.*?)$/)
50
+
51
+ def parse_summary_metadata
52
+ @src.pos += @src.matched_size
53
+ @root.options[:metadata][:summary] = @src[3]
54
+ end
55
+ define_parser(:summary_metadata, /^(Summary:)(\s+)(.*?)$/)
56
+ end
57
+ end
58
+ end
File without changes
@@ -0,0 +1,4 @@
1
+ module Aweplug
2
+ VERSION='1.0.0.a2'
3
+ end
4
+
data/lib/aweplug.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "aweplug/version"
2
+
3
+ module Aweplug
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Identity Collector' do
4
+
5
+ it 'may have one or more crawlers' do
6
+ pending "Not implemented"
7
+ end
8
+
9
+ it 'must have one storage' do
10
+ pending "Not implemented"
11
+ end
12
+
13
+ it 'may be configurable via a block' do
14
+ pending "Not implemented"
15
+ end
16
+
17
+ it 'may be configured via method calls' do
18
+ pending "Not implemented"
19
+ end
20
+
21
+ it 'must respond to execute(site)' do
22
+ pending "Not implemented"
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require 'aweplug/extensions/identity/github'
3
+
4
+ describe 'Github Crawler' do
5
+ let(:crawler) { Aweplug::Extensions::Identity::GitHub::Crawler.new }
6
+ it_should_behave_like 'a crawler'
7
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Storage' do
4
+
5
+ it 'must respond to write' do
6
+ pending "Not implemented"
7
+ end
8
+
9
+ it 'must respond to read' do
10
+ pending "Not implemented"
11
+ end
12
+
13
+ it 'must respond to fetch' do
14
+ pending "Not implemented"
15
+ end
16
+
17
+ it 'must respond to exist' do
18
+ pending "Not implemented"
19
+ end
20
+
21
+ it 'must respond to delete' do
22
+ pending "Not implemented"
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require 'rspec'
2
+
3
+ Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+
10
+ config.order = 'random'
11
+ end
@@ -0,0 +1,13 @@
1
+ shared_examples_for 'a crawler' do
2
+ it 'may require authentication' do
3
+ if crawler.respond_to? :authenticate_using, false
4
+ expect { crawler.crawl }.to raise_error
5
+ else
6
+ expect { crawler.crawl }.to_not raise_error
7
+ end
8
+ end
9
+
10
+ it 'must respond to crawl' do
11
+ should respond_to(:crawl)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aweplug
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.a2
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - LightGuard
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: octokit
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.24.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.24.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: faraday
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.8.7
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.7
46
+ - !ruby/object:Gem::Dependency
47
+ name: faraday_middleware
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.9.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: guard-rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 3.0.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 3.0.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 10.0.4
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 10.0.4
94
+ description: A set of Awestruct extensions for building a project website
95
+ email:
96
+ - lightguard.jp@gmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - .rspec
103
+ - Gemfile
104
+ - Guardfile
105
+ - LICENSE.txt
106
+ - README.adoc
107
+ - README.md
108
+ - Rakefile
109
+ - aweplug.gemspec
110
+ - lib/aweplug.rb
111
+ - lib/aweplug/extensions/identities.rb
112
+ - lib/aweplug/extensions/identity/confluence.rb
113
+ - lib/aweplug/extensions/identity/github.rb
114
+ - lib/aweplug/extensions/identity/gravatar.rb
115
+ - lib/aweplug/extensions/identity/jbosscommunity.rb
116
+ - lib/aweplug/extensions/kramdown_quickstart.rb
117
+ - lib/aweplug/extensions/sections.rb
118
+ - lib/aweplug/helpers/.gitkeep
119
+ - lib/aweplug/helpers/git_commit_metadata.rb
120
+ - lib/aweplug/helpers/kramdown_metadata.rb
121
+ - lib/aweplug/transformers/.gitkeep
122
+ - lib/aweplug/version.rb
123
+ - spec/aweplug/extensions/identity/collector_spec.rb
124
+ - spec/aweplug/extensions/identity/github_spec.rb
125
+ - spec/aweplug/extensions/identity/storage_spec.rb
126
+ - spec/spec_helper.rb
127
+ - spec/support/aweplug/extensions/identity/crawler_examples.rb
128
+ homepage: https://github.com/awestruct/aweplug
129
+ licenses: []
130
+ post_install_message:
131
+ rdoc_options: []
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>'
144
+ - !ruby/object:Gem::Version
145
+ version: 1.3.1
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 1.8.25
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: This set of Awestruct extensions includes helpful tools, extensions and
152
+ helpers for building a website using Awestruct. It includes extensions for accessing
153
+ Github, JIRA, managing identities and others.
154
+ test_files:
155
+ - spec/aweplug/extensions/identity/collector_spec.rb
156
+ - spec/aweplug/extensions/identity/github_spec.rb
157
+ - spec/aweplug/extensions/identity/storage_spec.rb
158
+ - spec/spec_helper.rb
159
+ - spec/support/aweplug/extensions/identity/crawler_examples.rb
160
+ has_rdoc: