meetup-generator 1.2.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +17 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +23 -0
- data/README.md +20 -0
- data/Rakefile +26 -0
- data/bin/meetup-generator.rb +31 -0
- data/config.ru +2 -0
- data/lib/all_the_things.yaml +817 -0
- data/lib/build_helpers.rb +9 -0
- data/lib/meetup-generator.rb +63 -0
- data/lib/version.rb +4 -0
- data/meetup-generator.gemspec +35 -0
- data/public/css/main.css +61 -0
- data/public/favicon.ico +0 -0
- data/public/zxspectr.woff +0 -0
- data/spec/acceptance_spec.rb +87 -0
- data/spec/unit_spec.rb +68 -0
- data/views/default.slim +48 -0
- data/website.xml +100 -0
- metadata +240 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# Everything needed for a meetup generator
|
5
|
+
#
|
6
|
+
class MeetupGenerator
|
7
|
+
attr_reader :words, :lib, :unused_templates
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
grep, dict = find_unix_stuff
|
11
|
+
@words = `#{grep} "^[a-z]*$" #{dict}`.split("\n")
|
12
|
+
@lib = YAML.load_file(ROOT + 'lib' + 'all_the_things.yaml')
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_unix_stuff
|
16
|
+
case RUBY_PLATFORM
|
17
|
+
when /solaris/
|
18
|
+
%w[/bin/grep /usr/share/lib/dict/words]
|
19
|
+
when /linux/
|
20
|
+
%w[/bin/grep /usr/share/dict/words]
|
21
|
+
when /darwin/
|
22
|
+
%w[/usr/bin/grep /usr/share/dict/words]
|
23
|
+
else
|
24
|
+
abort "unsupported platform: #{RUBY_PLATFORM}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def title
|
29
|
+
@unused_templates ||= lib[:template].dup
|
30
|
+
t = unused_templates.sample
|
31
|
+
unused_templates.delete(t)
|
32
|
+
t.scan(/%\w+%/).each { |k| t = t.sub(k, lib[k[1..-2].to_sym].sample) }
|
33
|
+
t.scan(/RAND\d+/).each do |i|
|
34
|
+
t = t.sub(i, rand(2..(i.sub(/RAND/, '').to_i)).to_s)
|
35
|
+
end
|
36
|
+
t
|
37
|
+
end
|
38
|
+
|
39
|
+
def talks(count = 5)
|
40
|
+
@unused_templates = lib[:template].dup
|
41
|
+
count.times.with_object([]) { |_i, a| a.<< title }
|
42
|
+
end
|
43
|
+
|
44
|
+
def talker
|
45
|
+
lib[:first_name].sample + ' ' + lib[:last_name].sample
|
46
|
+
end
|
47
|
+
|
48
|
+
def role
|
49
|
+
lib[:job_role].sample + ' ' + lib[:job_title].sample
|
50
|
+
end
|
51
|
+
|
52
|
+
def company
|
53
|
+
words.sample.sub(/([^aeiou])er$/, '\\1r').downcase + '.io'
|
54
|
+
end
|
55
|
+
|
56
|
+
def refreshment
|
57
|
+
lib[:food_style].sample + ' ' + lib[:food].sample
|
58
|
+
end
|
59
|
+
|
60
|
+
def talk
|
61
|
+
{ talk: talks(1)[0], talker: talker, role: role, company: company }
|
62
|
+
end
|
63
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'date'
|
3
|
+
require 'English'
|
4
|
+
require_relative 'lib/build_helpers'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'meetup-generator'
|
8
|
+
gem.version = gem_version
|
9
|
+
gem.date = Date.today.to_s
|
10
|
+
|
11
|
+
gem.summary = 'Stupid fatuous random string generatpr'
|
12
|
+
gem.description = 'Generates a website advertising a fictional ' \
|
13
|
+
'DevOps meetup, using all the latest buzzwords, ' \
|
14
|
+
'new-shiny, and clichés'
|
15
|
+
gem.authors = ['Robert Fisher']
|
16
|
+
gem.email = 'slackboy@gmail.com'
|
17
|
+
gem.homepage = 'https://github.com/snltd/meetup-generator'
|
18
|
+
gem.license = 'BSD-2-Clause'
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
21
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
22
|
+
|
23
|
+
gem.add_runtime_dependency 'sinatra', '~>2.0', '>= 2.0.1'
|
24
|
+
gem.add_runtime_dependency 'slim', '~> 3.0', '>= 3.0.0'
|
25
|
+
gem.add_runtime_dependency 'puma', '~> 3.11', '>= 3.11.0'
|
26
|
+
|
27
|
+
gem.add_development_dependency 'bundler', '~> 1.3'
|
28
|
+
gem.add_development_dependency 'minitest', '~> 5.11', '>= 5.11.0'
|
29
|
+
gem.add_development_dependency 'nokogiri', '~> 1.8', '>= 1.8.0'
|
30
|
+
gem.add_development_dependency 'rack-test', '~> 0.8', '>= 0.8.0'
|
31
|
+
gem.add_development_dependency 'rake', '~> 12.3', '>= 12.3.0'
|
32
|
+
gem.add_development_dependency 'rubocop', '~> 0.52', '>= 0.52.0'
|
33
|
+
|
34
|
+
gem.required_ruby_version = Gem::Requirement.new('>= 2.2.0')
|
35
|
+
end
|
data/public/css/main.css
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
@font-face {
|
2
|
+
font-family: 'ZX Spectrum';
|
3
|
+
src: url('/zxspectr.woff') format('woff');
|
4
|
+
}
|
5
|
+
|
6
|
+
body {
|
7
|
+
font-family: 'ZX Spectrum';
|
8
|
+
font-size: 0.9em;
|
9
|
+
background: #000;
|
10
|
+
color: #0f0;
|
11
|
+
line-height: 120%;
|
12
|
+
}
|
13
|
+
|
14
|
+
.tperson {
|
15
|
+
font-size: small;
|
16
|
+
margin-left: 12em;
|
17
|
+
}
|
18
|
+
|
19
|
+
ul {
|
20
|
+
list-style: none;
|
21
|
+
}
|
22
|
+
|
23
|
+
li {
|
24
|
+
padding-top: 1em;
|
25
|
+
text-indent: -9em;
|
26
|
+
margin-left: 9em;
|
27
|
+
}
|
28
|
+
|
29
|
+
.ttitle {
|
30
|
+
/*font-style: italic;*/
|
31
|
+
font-weight: bold;
|
32
|
+
background-color: #0ff;
|
33
|
+
color: #000;
|
34
|
+
}
|
35
|
+
|
36
|
+
h1 {
|
37
|
+
text-align: center;
|
38
|
+
padding-bottom: 0.5em;
|
39
|
+
margin-bottom: 1em;
|
40
|
+
color: #ff0;
|
41
|
+
line-height:100%;
|
42
|
+
border-bottom: 4px dotted #f0f;
|
43
|
+
}
|
44
|
+
|
45
|
+
#container {
|
46
|
+
padding-top: 3em;
|
47
|
+
width: 900px;
|
48
|
+
margin-left: auto;
|
49
|
+
margin-right: auto;
|
50
|
+
}
|
51
|
+
|
52
|
+
#footer {
|
53
|
+
margin-top: 20em;
|
54
|
+
font-size: x-small;
|
55
|
+
text-align: right;
|
56
|
+
}
|
57
|
+
|
58
|
+
a {
|
59
|
+
text-decoration: none;
|
60
|
+
color: #f0f;
|
61
|
+
}
|
data/public/favicon.ico
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Acceptance tests for the meetup generator. Yeah, I know.
|
4
|
+
#
|
5
|
+
%w[minitest minitest/unit minitest/autorun rack/test pathname json
|
6
|
+
yaml cgi nokogiri].each { |f| require f }
|
7
|
+
|
8
|
+
MGROOT = Pathname.new(__FILE__).realpath.dirname.parent
|
9
|
+
|
10
|
+
OUTER_APP = Rack::Builder.parse_file((MGROOT + 'config.ru').to_s).first
|
11
|
+
|
12
|
+
class TestApp < MiniTest::Test
|
13
|
+
attr_reader :things
|
14
|
+
include Rack::Test::Methods
|
15
|
+
|
16
|
+
def initialize(args)
|
17
|
+
super(args)
|
18
|
+
@things = YAML.load_file(MGROOT + 'lib' + 'all_the_things.yaml')
|
19
|
+
end
|
20
|
+
|
21
|
+
def app
|
22
|
+
OUTER_APP
|
23
|
+
end
|
24
|
+
|
25
|
+
# Load the page 1000 times and make sure no talk titles occur
|
26
|
+
# twice
|
27
|
+
#
|
28
|
+
def test_no_repeats
|
29
|
+
1000.times do
|
30
|
+
html = Nokogiri::HTML(get('/').body)
|
31
|
+
titles = html.css('span[class="ttitle"]').map(&:text)
|
32
|
+
assert_equal(titles, titles.uniq)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_default
|
37
|
+
#
|
38
|
+
# Get all of the templates used to generate talk titles, and re-run
|
39
|
+
# the test until we've seen them all. Then we know no template
|
40
|
+
# causes a crash. That's good enough.
|
41
|
+
#
|
42
|
+
templates = things[:template].map do |t|
|
43
|
+
escaped = Regexp.escape(CGI.escapeHTML(t))
|
44
|
+
matcher = escaped.gsub(/%\w+%/, '\w+').gsub(/RAND\d+/, '\d+')
|
45
|
+
Regexp.new('^.*ttitle">' + matcher + '</span>.*$')
|
46
|
+
end
|
47
|
+
|
48
|
+
until templates.empty?
|
49
|
+
get '/'
|
50
|
+
assert last_response.ok?
|
51
|
+
assert last_response.header['Content-Type'] == 'text/html;charset=utf-8'
|
52
|
+
x = last_response.body
|
53
|
+
assert_match(/The code./, x)
|
54
|
+
assert_match(/DevOps Meetup/, x)
|
55
|
+
templates.each { |t| templates.delete(t) if x.match(t) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_api_talk
|
60
|
+
get '/api/talk'
|
61
|
+
assert last_response.ok?
|
62
|
+
assert last_response.header['Content-Type'] == 'application/json'
|
63
|
+
resp = last_response.body
|
64
|
+
refute_empty resp
|
65
|
+
j = JSON.parse(resp)
|
66
|
+
fields = %w[talk talker role company]
|
67
|
+
assert_equal(j.keys, fields)
|
68
|
+
fields.each { |f| refute_empty j[f] }
|
69
|
+
name = j['talker'].split
|
70
|
+
assert_includes(things[:first_name], name.first)
|
71
|
+
assert_includes(things[:last_name], name.last)
|
72
|
+
assert j['company'].end_with?('.io')
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_api_misc
|
76
|
+
%w[title talker company role refreshment].each do |word|
|
77
|
+
get format('/api/%s', word)
|
78
|
+
assert last_response.ok?
|
79
|
+
assert last_response.header['Content-Type'] == 'application/json'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_api_404
|
84
|
+
get '/api/no_such_thing'
|
85
|
+
assert_equal(last_response.status, 404)
|
86
|
+
end
|
87
|
+
end
|
data/spec/unit_spec.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative '../lib/meetup-generator'
|
5
|
+
|
6
|
+
THINGS = { food_style: %w[artisan],
|
7
|
+
food: %w[flatbread],
|
8
|
+
first_name: %w[John],
|
9
|
+
last_name: %w[Smith],
|
10
|
+
job_role: %w[Neckbeard],
|
11
|
+
job_title: ['Without Portfolio'],
|
12
|
+
tech: %w[Ruby],
|
13
|
+
template: ['RAND20 %tech% things'] }.freeze
|
14
|
+
|
15
|
+
class Giblets < MeetupGenerator
|
16
|
+
def initialize; end
|
17
|
+
end
|
18
|
+
|
19
|
+
class GibletsTest < MiniTest::Test
|
20
|
+
attr_reader :m
|
21
|
+
|
22
|
+
def setup
|
23
|
+
@m = Giblets.new
|
24
|
+
m.instance_variable_set(:@lib, THINGS)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_title
|
28
|
+
x = m.title
|
29
|
+
assert_instance_of(String, x)
|
30
|
+
assert !x.empty?
|
31
|
+
refute_match(/%\w+%/, x)
|
32
|
+
refute_match(/RAND/, x)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_talk
|
36
|
+
x = m.talks(1)
|
37
|
+
assert_instance_of(Array, x)
|
38
|
+
assert_instance_of(String, x.first)
|
39
|
+
toks = x.first.split
|
40
|
+
number = toks.first.to_i
|
41
|
+
assert number > 0
|
42
|
+
assert number <= 20
|
43
|
+
assert_equal('Ruby', toks[1])
|
44
|
+
assert_equal('things', toks[2])
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_talker
|
48
|
+
assert_equal(m.talker, 'John Smith')
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_role
|
52
|
+
assert_equal(m.role, 'Neckbeard Without Portfolio')
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_company_no_e
|
56
|
+
m.instance_variable_set(:@words, %w[leadswinger])
|
57
|
+
assert_equal(m.company, 'leadswingr.io')
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_company
|
61
|
+
m.instance_variable_set(:@words, %w[Cabbage])
|
62
|
+
assert_equal(m.company, 'cabbage.io')
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_refreshment
|
66
|
+
assert_equal(m.refreshment, 'artisan flatbread')
|
67
|
+
end
|
68
|
+
end
|
data/views/default.slim
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
doctype html
|
2
|
+
html
|
3
|
+
head
|
4
|
+
title #Devops Meetup
|
5
|
+
meta charset='utf-8'
|
6
|
+
link href='/css/main.css' rel='stylesheet'
|
7
|
+
|
8
|
+
body
|
9
|
+
#container
|
10
|
+
h1 #DevOps Meetup // Shoreditch, probably // #{(Date.today + 1).strftime("%d/%m/%Y")}
|
11
|
+
|
12
|
+
ul
|
13
|
+
li 18:00 // Introduction
|
14
|
+
|
15
|
+
li
|
16
|
+
' 18:10 // Lightning talk //
|
17
|
+
span.ttitle=@talks.pop
|
18
|
+
.tperson=@jobs.pop
|
19
|
+
|
20
|
+
li
|
21
|
+
' 18:20 //
|
22
|
+
span.ttitle=@talks.pop
|
23
|
+
.tperson=@jobs.pop
|
24
|
+
|
25
|
+
li 18:50 // break
|
26
|
+
.tperson=@food
|
27
|
+
|
28
|
+
li
|
29
|
+
' 19:20 //
|
30
|
+
span.ttitle=@talks.pop
|
31
|
+
.tperson=@jobs.pop
|
32
|
+
|
33
|
+
li
|
34
|
+
' 19:40 // Ignite //
|
35
|
+
span.ttitle=@talks.pop
|
36
|
+
.tperson=@jobs.pop
|
37
|
+
|
38
|
+
li
|
39
|
+
' 20:00 //
|
40
|
+
span.ttitle=@talks.pop
|
41
|
+
.tperson=@jobs.pop
|
42
|
+
|
43
|
+
li
|
44
|
+
' 20:30 // Close
|
45
|
+
.tperson Everyone is hiring, but no one's paying
|
46
|
+
|
47
|
+
#footer
|
48
|
+
a href='https://github.com/snltd/meetup-generator' The code.
|
data/website.xml
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
<?xml version='1.0'?>
|
2
|
+
|
3
|
+
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
|
4
|
+
|
5
|
+
<service_bundle type='manifest' name='snltd:sinatra:meetup-generator'>
|
6
|
+
|
7
|
+
<service
|
8
|
+
name='application/sinatra/meetup-generator'
|
9
|
+
type='service'
|
10
|
+
version='1'>
|
11
|
+
|
12
|
+
<single_instance />
|
13
|
+
|
14
|
+
<property_group name='startd' type='framework'>
|
15
|
+
|
16
|
+
<stability value='Unstable' />
|
17
|
+
|
18
|
+
<propval
|
19
|
+
name='duration'
|
20
|
+
type='astring'
|
21
|
+
value='transient' />
|
22
|
+
|
23
|
+
</property_group>
|
24
|
+
|
25
|
+
<property_group name='options' type='applications'>
|
26
|
+
|
27
|
+
<propval
|
28
|
+
name='rackup'
|
29
|
+
type='astring'
|
30
|
+
value='/opt/local/bin/rackup' />
|
31
|
+
|
32
|
+
<propval
|
33
|
+
name='user'
|
34
|
+
type='astring'
|
35
|
+
value='sinatra' />
|
36
|
+
|
37
|
+
<propval
|
38
|
+
name='dir'
|
39
|
+
type='astring'
|
40
|
+
value='/www/meetup-generator' />
|
41
|
+
|
42
|
+
<propval
|
43
|
+
name='port'
|
44
|
+
type='astring'
|
45
|
+
value='8002' />
|
46
|
+
|
47
|
+
</property_group>
|
48
|
+
|
49
|
+
<instance name='default' enabled='true'>
|
50
|
+
|
51
|
+
<!-- Run in multi-user mode -->
|
52
|
+
|
53
|
+
<dependency
|
54
|
+
name='multi-user'
|
55
|
+
grouping='require_all'
|
56
|
+
restart_on='none'
|
57
|
+
type='service'>
|
58
|
+
|
59
|
+
<service_fmri value='svc:/milestone/multi-user-server:default' />
|
60
|
+
|
61
|
+
</dependency>
|
62
|
+
|
63
|
+
<exec_method
|
64
|
+
name='start'
|
65
|
+
type='method'
|
66
|
+
timeout_seconds='0'
|
67
|
+
exec='%{options/rackup} -D -p %{options/port} %{options/dir}/config.ru'>
|
68
|
+
<method_context>
|
69
|
+
<method_credential
|
70
|
+
user='%{options/user}'
|
71
|
+
group='nogroup'
|
72
|
+
privileges='basic,!proc_session,!proc_info,!file_link_any'/>
|
73
|
+
</method_context>
|
74
|
+
</exec_method>
|
75
|
+
|
76
|
+
<exec_method
|
77
|
+
name='stop'
|
78
|
+
type='method'
|
79
|
+
timeout_seconds='10'
|
80
|
+
exec='pkill -2 -f "%{options/rackup}.*-p %{options/port}"' />
|
81
|
+
|
82
|
+
</instance>
|
83
|
+
|
84
|
+
<template>
|
85
|
+
|
86
|
+
<common_name>
|
87
|
+
<loctext xml:lang='C'>
|
88
|
+
meetup-generator website
|
89
|
+
</loctext>
|
90
|
+
</common_name>
|
91
|
+
|
92
|
+
<documentation>
|
93
|
+
<doc_link name='meetup.rfisher.co.uk' uri='http://meetup.rdfisher.co.uk' />
|
94
|
+
</documentation>
|
95
|
+
|
96
|
+
</template>
|
97
|
+
|
98
|
+
</service>
|
99
|
+
|
100
|
+
</service_bundle>
|