meetup-generator 0.0.20210128
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/Gemfile +4 -0
- data/Gemfile.lock +84 -0
- data/LICENSE.txt +23 -0
- data/README.md +64 -0
- data/Rakefile +19 -0
- data/bin/locate_meetup-generator +6 -0
- data/bin/meetup-generator.rb +34 -0
- data/config.ru +4 -0
- data/lib/all_the_things.yaml +1127 -0
- data/lib/locator.rb +7 -0
- data/lib/meetup_generator.rb +106 -0
- data/lib/words.gz +0 -0
- data/meetup-generator.gemspec +43 -0
- data/package/meetup-generator.xml +100 -0
- data/public/css/main.css +64 -0
- data/public/favicon.ico +0 -0
- data/public/zxspectr.woff +0 -0
- data/spec/acceptance_spec.rb +116 -0
- data/spec/meetup_generator_spec.rb +138 -0
- data/views/default.slim +48 -0
- data/views/partial_talk.slim +11 -0
- metadata +236 -0
    
        data/lib/locator.rb
    ADDED
    
    
| @@ -0,0 +1,106 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'yaml'
         | 
| 4 | 
            +
            require 'date'
         | 
| 5 | 
            +
            require 'zlib'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            LIB = Pathname.new(__dir__)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            # Everything needed for a meetup generator
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            class MeetupGenerator
         | 
| 12 | 
            +
              attr_reader :words, :lib
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def initialize
         | 
| 15 | 
            +
                @words = Zlib::GzipReader.open(LIB + 'words.gz').readlines.map(&:strip)
         | 
| 16 | 
            +
                @lib   = YAML.safe_load(IO.read(LIB + 'all_the_things.yaml'),
         | 
| 17 | 
            +
                                        symbolize_names: true)
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              # @param num [Integer] how many talks you want
         | 
| 21 | 
            +
              # @return [Hash] full meetup agenda
         | 
| 22 | 
            +
              #
         | 
| 23 | 
            +
              def agenda(num = 5)
         | 
| 24 | 
            +
                { talks: lib[:template].sample(num).map { |t| talk(t) },
         | 
| 25 | 
            +
                  refreshment: refreshment,
         | 
| 26 | 
            +
                  location: location,
         | 
| 27 | 
            +
                  date: date }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              # @param templates [Array[String]] array of templates
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              def talk(template = random_template)
         | 
| 33 | 
            +
                { title: title(template),
         | 
| 34 | 
            +
                  talker: talker,
         | 
| 35 | 
            +
                  role: role,
         | 
| 36 | 
            +
                  company: company }
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def location
         | 
| 40 | 
            +
                'Shoreditch, probably'
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def date
         | 
| 44 | 
            +
                (Date.today + 1).strftime('%d/%m/%Y')
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def title(template = random_template)
         | 
| 48 | 
            +
                replace_word(replace_number(replace_ops(replace_things(template))))
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def random_template(number = 1)
         | 
| 52 | 
            +
                lib[:template].sample(number).first
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def talker
         | 
| 56 | 
            +
                pair(:first_name, :last_name)
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def role
         | 
| 60 | 
            +
                pair(:job_role, :job_title)
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def refreshment
         | 
| 64 | 
            +
                pair(:food_style, :food)
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              def company
         | 
| 68 | 
            +
                format('%<word>s.io', word: words.sample.sub(/([^aeiou])er$/, '\\1r'))
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def something_ops
         | 
| 72 | 
            +
                (lib[:something_ops] * 4).sample(rand(2..4)).join + 'Ops'
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def pair(list1, list2)
         | 
| 76 | 
            +
                [lib[list1].sample, lib[list2].sample].join(' ')
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def replace_things(template)
         | 
| 80 | 
            +
                return template unless template =~ /%[a-z_]+%/
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                replace_things(template.sub(/%([a-z_]+)%/) do
         | 
| 83 | 
            +
                  lib[Regexp.last_match(1).to_sym].sample
         | 
| 84 | 
            +
                end)
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              def replace_word(template)
         | 
| 88 | 
            +
                return template unless template.include?('%WORD%')
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                replace_word(template.sub('%WORD%', words.sample.capitalize))
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def replace_ops(template)
         | 
| 94 | 
            +
                return template unless template.include?('%FNOPS%')
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                replace_ops(template.sub('%FNOPS%', something_ops))
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              def replace_number(template)
         | 
| 100 | 
            +
                return template unless template =~ /%RAND\d+%/
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                replace_number(template.sub(/%RAND(\d+)%/) do
         | 
| 103 | 
            +
                  rand(2..Regexp.last_match(1).to_i).to_s
         | 
| 104 | 
            +
                end)
         | 
| 105 | 
            +
              end
         | 
| 106 | 
            +
            end
         | 
    
        data/lib/words.gz
    ADDED
    
    | Binary file | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'date'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # Github actions sets the RELEASE_VERSION environment variable
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Gem::Specification.new do |gem|
         | 
| 8 | 
            +
              gem.name          = 'meetup-generator'
         | 
| 9 | 
            +
              gem.version       = ENV['RELEASE_VERSION'] ||
         | 
| 10 | 
            +
                                  "0.0.#{Time.now.strftime('%Y%m%d')}"
         | 
| 11 | 
            +
              gem.date          = Date.today.to_s
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              gem.summary       = 'Stupid fatuous random string generatpr'
         | 
| 14 | 
            +
              gem.description   = 'Generates a website advertising a fictional ' \
         | 
| 15 | 
            +
                                  'DevOps meetup, using all the latest buzzwords, ' \
         | 
| 16 | 
            +
                                  'new-shiny, and clichés'
         | 
| 17 | 
            +
              gem.authors       = ['Robert Fisher']
         | 
| 18 | 
            +
              gem.email         = 'rob@sysdef.xyz'
         | 
| 19 | 
            +
              gem.homepage      = 'https://github.com/snltd/meetup-generator'
         | 
| 20 | 
            +
              gem.license       = 'BSD-2-Clause'
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              gem.bindir        = 'bin'
         | 
| 23 | 
            +
              gem.executables   = ['meetup-generator.rb', 'locate_meetup-generator']
         | 
| 24 | 
            +
              gem.files         = IO.readlines('package/runtime_files', chomp: true)
         | 
| 25 | 
            +
              gem.test_files    = gem.files.grep(%r{^spec/})
         | 
| 26 | 
            +
              gem.require_paths = %w[lib]
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              gem.add_runtime_dependency 'puma', '~> 5.1'
         | 
| 29 | 
            +
              gem.add_runtime_dependency 'sinatra', '~>2.1'
         | 
| 30 | 
            +
              gem.add_runtime_dependency 'slim', '~> 4.1'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              gem.add_development_dependency 'bundler', '~> 2.0'
         | 
| 33 | 
            +
              gem.add_development_dependency 'minitest', '~> 5.14'
         | 
| 34 | 
            +
              gem.add_development_dependency 'nokogiri', '~> 1.10'
         | 
| 35 | 
            +
              gem.add_development_dependency 'rack-test', '~> 1.1'
         | 
| 36 | 
            +
              gem.add_development_dependency 'rake', '~> 13.0'
         | 
| 37 | 
            +
              gem.add_development_dependency 'rubocop', '~> 1.9'
         | 
| 38 | 
            +
              gem.add_development_dependency 'rubocop-minitest', '~> 0.10'
         | 
| 39 | 
            +
              gem.add_development_dependency 'rubocop-performance', '~> 1.3'
         | 
| 40 | 
            +
              gem.add_development_dependency 'rubocop-rake', '~> 0.5'
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              gem.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
         | 
| 43 | 
            +
            end
         | 
| @@ -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='sysdef/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.sysdef.xyz' uri='http://meetup.sysdef.xyz' />
         | 
| 94 | 
            +
            	</documentation>
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            </template>
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            </service>
         | 
| 99 | 
            +
             | 
| 100 | 
            +
            </service_bundle>
         | 
    
        data/public/css/main.css
    ADDED
    
    | @@ -0,0 +1,64 @@ | |
| 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 | 
            +
            .indent {
         | 
| 15 | 
            +
              font-size: small;
         | 
| 16 | 
            +
              margin-left: 12em;
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            .indent a {
         | 
| 20 | 
            +
              color: #0f0;
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ul {
         | 
| 24 | 
            +
              list-style: none;
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            li {
         | 
| 28 | 
            +
              padding-top: 1em;
         | 
| 29 | 
            +
              text-indent: -9em;
         | 
| 30 | 
            +
              margin-left: 9em;
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            .ttitle {
         | 
| 34 | 
            +
              font-weight: bold;
         | 
| 35 | 
            +
              background-color: #0ff;
         | 
| 36 | 
            +
              color: #000;
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            h1 {
         | 
| 40 | 
            +
              text-align: center;
         | 
| 41 | 
            +
              padding-bottom: 0.5em;
         | 
| 42 | 
            +
              margin-bottom: 1em;
         | 
| 43 | 
            +
              color: #ff0;
         | 
| 44 | 
            +
              line-height:100%;
         | 
| 45 | 
            +
              border-bottom: 4px dotted #f0f;
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            #container {
         | 
| 49 | 
            +
              padding-top: 3em;
         | 
| 50 | 
            +
              width: 900px;
         | 
| 51 | 
            +
              margin-left: auto;
         | 
| 52 | 
            +
              margin-right: auto;
         | 
| 53 | 
            +
            }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            #footer {
         | 
| 56 | 
            +
              margin-top: 20em;
         | 
| 57 | 
            +
              font-size: x-small;
         | 
| 58 | 
            +
              text-align: right;
         | 
| 59 | 
            +
            }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            a {
         | 
| 62 | 
            +
              text-decoration: none;
         | 
| 63 | 
            +
              color: #f0f;
         | 
| 64 | 
            +
            }
         | 
    
        data/public/favicon.ico
    ADDED
    
    | Binary file | 
| Binary file | 
| @@ -0,0 +1,116 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            %w[minitest minitest/unit minitest/autorun rack/test pathname json
         | 
| 5 | 
            +
               yaml cgi nokogiri].each { |f| require f }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            MGROOT = Pathname.new(__FILE__).realpath.dirname.parent
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            OUTER_APP = Rack::Builder.parse_file((MGROOT + 'config.ru').to_s).first
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            # Acceptance tests for the meetup generator. Yeah, I know.
         | 
| 12 | 
            +
            #
         | 
| 13 | 
            +
            class TestApp < MiniTest::Test
         | 
| 14 | 
            +
              attr_reader :things
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              include Rack::Test::Methods
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def initialize(args)
         | 
| 19 | 
            +
                super(args)
         | 
| 20 | 
            +
                @things = YAML.safe_load(IO.read(MGROOT + 'lib' + 'all_the_things.yaml'),
         | 
| 21 | 
            +
                                         symbolize_names: true)
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def app
         | 
| 25 | 
            +
                OUTER_APP
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              # Load the page a thousand times and make sure no talk titles
         | 
| 29 | 
            +
              # occur twice
         | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              def test_no_repeats
         | 
| 32 | 
            +
                1000.times do
         | 
| 33 | 
            +
                  html = Nokogiri::HTML(get('/').body)
         | 
| 34 | 
            +
                  titles = html.css('span[class="ttitle"]').map(&:text)
         | 
| 35 | 
            +
                  assert_equal(titles, titles.uniq)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              # Get all of the templates used to generate talk titles, and
         | 
| 40 | 
            +
              # re-run the test until we've seen them all. Then we know no
         | 
| 41 | 
            +
              # template causes a crash. That's good enough.
         | 
| 42 | 
            +
              #
         | 
| 43 | 
            +
              def test_default
         | 
| 44 | 
            +
                templates = things[:template].map do |t|
         | 
| 45 | 
            +
                  escaped = Regexp.escape(CGI.escapeHTML(t))
         | 
| 46 | 
            +
                  matcher = escaped.gsub(/%\w+%/, '[\w\-]+').gsub(/RAND\d+/, '\d+')
         | 
| 47 | 
            +
                                   .gsub(/FNOPS/, '\w+')
         | 
| 48 | 
            +
                  Regexp.new('^.*ttitle">' + matcher + '</span>.*$')
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                until templates.empty?
         | 
| 52 | 
            +
                  get '/'
         | 
| 53 | 
            +
                  assert_equal('text/html;charset=utf-8',
         | 
| 54 | 
            +
                               last_response.header['Content-Type'])
         | 
| 55 | 
            +
                  last_response.ok?
         | 
| 56 | 
            +
                  resp = last_response.body
         | 
| 57 | 
            +
                  assert_match(/The code./, resp)
         | 
| 58 | 
            +
                  assert_match(/DevOps Meetup/, resp)
         | 
| 59 | 
            +
                  templates.each { |t| templates.delete(t) if resp.match?(t) }
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def test_api_talker
         | 
| 64 | 
            +
                get format('/api/talker')
         | 
| 65 | 
            +
                assert last_response.ok?
         | 
| 66 | 
            +
                assert_instance_of(String, last_response.body)
         | 
| 67 | 
            +
                assert_match(/^"\w+ \w+"$/, last_response.body)
         | 
| 68 | 
            +
                assert last_response.header['Content-Type'] == 'application/json'
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def test_api_company
         | 
| 72 | 
            +
                get format('/api/company')
         | 
| 73 | 
            +
                assert last_response.ok?
         | 
| 74 | 
            +
                assert_instance_of(String, last_response.body)
         | 
| 75 | 
            +
                assert_match(/^"\w+.io"$/, last_response.body)
         | 
| 76 | 
            +
                assert last_response.header['Content-Type'] == 'application/json'
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def test_api_misc
         | 
| 80 | 
            +
                %w[title role refreshment location date].each do |word|
         | 
| 81 | 
            +
                  get format('/api/%<word>s', word: word)
         | 
| 82 | 
            +
                  assert last_response.ok?
         | 
| 83 | 
            +
                  assert_instance_of(String, last_response.body)
         | 
| 84 | 
            +
                  assert last_response.header['Content-Type'] == 'application/json'
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              def test_api_talk
         | 
| 89 | 
            +
                get format('/api/talk')
         | 
| 90 | 
            +
                assert last_response.ok?
         | 
| 91 | 
            +
                assert_instance_of(String, last_response.body)
         | 
| 92 | 
            +
                as_obj = JSON.parse(last_response.body, symbolize_names: true)
         | 
| 93 | 
            +
                assert_equal(%i[title talker role company], as_obj.keys)
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              def test_api_agenda
         | 
| 97 | 
            +
                get format('/api/agenda')
         | 
| 98 | 
            +
                assert last_response.ok?
         | 
| 99 | 
            +
                assert_instance_of(String, last_response.body)
         | 
| 100 | 
            +
                agenda = JSON.parse(last_response.body, symbolize_names: true)
         | 
| 101 | 
            +
                assert_equal(%i[date location refreshment talks], agenda.keys.sort)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                agenda[:talks].each do |t|
         | 
| 104 | 
            +
                  assert_equal(%i[title talker role company], t.keys)
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                assert_instance_of(String, agenda[:refreshment])
         | 
| 108 | 
            +
                assert_instance_of(String, agenda[:date])
         | 
| 109 | 
            +
                assert_instance_of(String, agenda[:location])
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
              def test_api_not_found
         | 
| 113 | 
            +
                get '/api/no_such_thing'
         | 
| 114 | 
            +
                assert_equal(last_response.status, 404)
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
            end
         |