event_reporter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (C) 2013 Raphael Weiner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Event Reporter
2
+
3
+ Allows for inspection, filtering, printing and saving of csv containing event attendees through CLI.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'event_reporter'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install event_reporter
18
+
19
+ ## Usage
20
+
21
+ For help:
22
+ $ help
23
+
24
+ Example:
25
+ $ load
26
+ $ find first_name (chris, sarah) and state (co, ky)
27
+ $ add last_name smith
28
+ $ queue print by last_name
29
+ $ queue save to results.csv
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ ,RegDate,first_Name,last_Name,Email_Address,HomePhone,Street,City,State,Zipcode
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift("lib")
3
+
4
+ require 'event_reporter/runner'
5
+
6
+ EventReporter::Runner.new
data/bin/help.yml ADDED
@@ -0,0 +1,10 @@
1
+ find : find <attribute> <criteria> searches csv for specific criteria
2
+ load : load <filename> erase any loaded data and parse the specified file. If no filename is given default to event_attendees.csv
3
+ queue_count : queue count outputs how many records are in the current queue
4
+ queue_clear : queue clear empty the queue
5
+ queue_print : queue print print out a tab-delimited data table with a header row
6
+ queue_print_by : queue print by <attribute> print the data table sorted by the specified attribute like zipcode
7
+ queue_save_to : queue save to <filename.csv> exports current queue to specified filename as a CSV
8
+ add : add <attribute> <criteria> exports current queue to specified filename as a CSV
9
+ subtract : subtract <attribute> <criteria> exports current queue to specified filename as a CSV
10
+ quit : quit exits program
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "event_reporter"
3
+ gem.version = "1.0.0"
4
+ gem.author = "Raphael Weiner"
5
+ gem.email = "raphael.weiner@gmail.com"
6
+ gem.summary = "Loads csv of event attendees and allows for searching"
7
+ gem.description = %q{
8
+ Allows for inspection, filtering, printing and saving of csv containing
9
+ list of event attendees through CLI.
10
+ }
11
+ gem.homepage = "http://github.com/raphweiner/event_reporter"
12
+
13
+ gem.files = Dir["{bin,lib}/**/*"] + %w(
14
+ README.md
15
+ LICENSE
16
+ event_reporter.gemspec
17
+ )
18
+ gem.executables = [ 'event_reporter' ]
19
+
20
+ gem.required_ruby_version = '>=1.9'
21
+ gem.has_rdoc = false
22
+ # gem.add_dependency 'CSV'
23
+ # gem.add_dependency 'yaml'
24
+ gem.add_dependency 'json'
25
+ gem.add_dependency 'nokogiri'
26
+ end
@@ -0,0 +1,32 @@
1
+ module EventReporter
2
+ module AcceptAnyInput
3
+ class << self
4
+ def read_char
5
+ begin
6
+ old_state = `stty -g`
7
+ system "stty raw -echo"
8
+ c = get_char
9
+ rescue => ex
10
+ puts "#{ex.class}: #{ex.message}"
11
+ puts ex.backtrace
12
+ ensure
13
+ system "stty #{old_state}"
14
+ end
15
+ return c
16
+ end
17
+
18
+ def get_char
19
+ c = STDIN.getc.chr
20
+ if(c=="\e")
21
+ extra_thread = Thread.new{
22
+ c = c + STDIN.getc.chr
23
+ c = c + STDIN.getc.chr
24
+ }
25
+ extra_thread.join(0.00001)
26
+ extra_thread.kill
27
+ end
28
+ c
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module EventReporter
2
+ Attendee = Struct.new(:last_name,
3
+ :first_name,
4
+ :email,
5
+ :zipcode,
6
+ :city,
7
+ :state,
8
+ :address,
9
+ :phone_number
10
+ )
11
+ end
@@ -0,0 +1,42 @@
1
+ module EventReporter
2
+ class Cache
3
+ attr_accessor :queue
4
+ include ParseInput
5
+
6
+ def initialize
7
+ @queue = []
8
+ end
9
+
10
+ def route_queue(input)
11
+ command, leftovers = parse_input(input)
12
+ method = "queue_#{command}"
13
+ if respond_to? method
14
+ send(method, leftovers)
15
+ else
16
+ throw(:top, "Unknown queue command '#{command}'")
17
+ end
18
+ end
19
+
20
+ def queue_count(leftovers)
21
+ puts "There are #{@queue.length} records in your queue"
22
+ end
23
+
24
+ def queue_clear(leftovers)
25
+ @queue = []
26
+ puts "Queue is cleared"
27
+ end
28
+
29
+ def queue_print(leftovers)
30
+ if leftovers[0] == 'by'
31
+ Print.queue_print_by(leftovers[1], @queue)
32
+ else
33
+ Print.queue_print(@queue)
34
+ end
35
+ end
36
+
37
+ def queue_save(leftovers)
38
+ throw(:top, "Unknown save command") if leftovers[0] != 'to'
39
+ Save.route_save_to(leftovers[1], @queue)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ module EventReporter
2
+ module Cleaners
3
+ class << self
4
+ def clean_name(name)
5
+ name.to_s.chomp.downcase.capitalize
6
+ end
7
+
8
+ def clean_email(email)
9
+ email.to_s
10
+ end
11
+
12
+ def clean_zipcode(zipcode)
13
+ zipcode.to_s.rjust(5,"0")[0..4]
14
+ end
15
+
16
+ def clean_city(city)
17
+ city.to_s.downcase.chomp.capitalize
18
+ end
19
+
20
+ def clean_state(state)
21
+ state.to_s[0..1].upcase
22
+ end
23
+
24
+ def clean_address(address)
25
+ address.to_s
26
+ end
27
+
28
+ def clean_phone_number(phone_number)
29
+ cleaned_number = phone_number.scan(/\d/).join("")
30
+ number_length = cleaned_number.length
31
+ if number_length == 10
32
+ cleaned_number
33
+ elsif number_length == 11 and phone_number[0] == '1'
34
+ phone_number[1..10]
35
+ else
36
+ '0'*10
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module EventReporter
2
+ class CSVParser
3
+ def initialize(filename)
4
+ csv_file = File.join(File.dirname(__FILE__), "../../bin/#{filename}")
5
+ throw(:top, "File not found :(") if !File.exists?(csv_file)
6
+ @contents = CSV.open(csv_file,
7
+ :headers => true,
8
+ :header_converters => :symbol)
9
+ end
10
+
11
+ def parse
12
+ @contents.map do |line|
13
+ last_name = Cleaners::clean_name(line[:last_name])
14
+ first_name = Cleaners::clean_name(line[:first_name])
15
+ email = Cleaners::clean_email(line[:email_address])
16
+ zipcode = Cleaners::clean_zipcode(line[:zipcode])
17
+ city = Cleaners::clean_city(line[:city])
18
+ state = Cleaners::clean_state(line[:state])
19
+ address = Cleaners::clean_address(line[:street])
20
+ phone_number = Cleaners::clean_phone_number(line[:homephone])
21
+ Attendee.new(last_name,
22
+ first_name,
23
+ email,
24
+ zipcode,
25
+ city,
26
+ state,
27
+ address,
28
+ phone_number
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,116 @@
1
+ module EventReporter
2
+ class Database
3
+ include ParseInput
4
+
5
+ def initialize
6
+ @attendees = []
7
+ @cache = Cache.new
8
+ end
9
+
10
+ def route_queue(input)
11
+ command, leftovers = parse_input(input)
12
+ if command == 'find'
13
+ route_find('queue', leftovers)
14
+ else
15
+ @cache.route_queue(input)
16
+ end
17
+ end
18
+
19
+ def load_csv(filename)
20
+ filename = filename.empty? ? "event_attendees.csv" : filename[0]
21
+ @attendees = CSVParser.new(filename).parse
22
+
23
+ puts "Loaded #{filename} (#{@attendees.length} attendees)"
24
+ end
25
+
26
+ def route_find(scope, instructions)
27
+ throw(:top, "Your database is empty (try 'load')") if @attendees.empty?
28
+ @cache.queue = if instructions.include?("and")
29
+ advanced_find(scope, instructions, "and")
30
+ elsif instructions.include?("or")
31
+ advanced_find(scope, instructions, "or")
32
+ else
33
+ initiate_search(scope, instructions)
34
+ end
35
+
36
+ @cache.queue_count("")
37
+ # remove parameter after Frank's feedback
38
+ end
39
+
40
+ def advanced_find(scope, instructions, logic)
41
+ instructions = instructions.join(" ").split(" #{logic} ")
42
+
43
+ search1 = instructions[0].split(" ")
44
+ search2 = instructions[1].split(" ")
45
+
46
+ result1 = initiate_search(scope, search1)
47
+ result2 = initiate_search(scope, search2)
48
+
49
+ if logic == "and"
50
+ return result1 & result2
51
+ elsif logic == "or"
52
+ return result1 | result2
53
+ end
54
+ end
55
+
56
+ def initiate_search(scope, input)
57
+ throw(:top, "Incorrect find input") if input.length < 2
58
+
59
+ attribute, criterias = parse_atts_crits(input)
60
+
61
+ results = criterias.map {|criteria| search(scope, attribute, criteria)}
62
+ results.reduce(&:|)
63
+ end
64
+
65
+ def parse_atts_crits(input)
66
+ attribute = input[0]
67
+ criterias = input[1..-1].join(" ").split(",")
68
+
69
+ criterias.map! do |criteria|
70
+ criteria.gsub("(", "").gsub(")", "").strip
71
+ end
72
+
73
+ validate_input(attribute, criterias)
74
+ return attribute, criterias
75
+ end
76
+
77
+ def validate_input(attribute, criterias)
78
+ if not HEADERS.include? attribute
79
+ throw(:top, "Attribute '#{attribute}' not found")
80
+ elsif criterias.nil? || criterias.empty?
81
+ throw(:top, "Please enter a find criteria")
82
+ end
83
+ end
84
+
85
+ def search(scope, attribute, criteria)
86
+ db = scope == 'all' ? @attendees : @cache.queue
87
+ db.select do |attendee|
88
+ criteria.chomp.downcase == attendee[attribute].downcase
89
+ end
90
+ end
91
+
92
+ def subtract(instructions)
93
+ attribute, criteria = parse_maths_input(instructions)
94
+ @cache.queue.delete_if do |attendee|
95
+ attendee[attribute].downcase == criteria.downcase
96
+ end
97
+ end
98
+
99
+ def add(instructions)
100
+ attribute, criteria = parse_maths_input(instructions)
101
+ @cache.queue |= search('all', attribute, criteria)
102
+ end
103
+
104
+ def parse_maths_input(instructions)
105
+ if instructions.length != 2
106
+ throw(:top, "Incorrect add/subtract input")
107
+ elsif @cache.queue.empty?
108
+ throw(:top, "Can't add/subtract from an empty queue, silly!")
109
+ end
110
+
111
+ attribute, criteria = instructions[0], instructions[1]
112
+ validate_input(attribute, criteria)
113
+ return attribute, criteria
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,9 @@
1
+ module EventReporter
2
+ module ParseInput
3
+ def parse_input(input)
4
+ command = input[0] || []
5
+ leftovers = input[1..-1] || []
6
+ return command, leftovers
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ module EventReporter
2
+ module Print
3
+ class << self
4
+ def find_column_lengths(queue)
5
+ queue.inject(new_length_hash) do |column_length, attendee|
6
+ HEADERS.each do |header|
7
+ header = header.to_sym
8
+
9
+ if attendee[header].length > column_length[header]
10
+ column_length[header] = attendee[header].length
11
+ end
12
+ end
13
+
14
+ column_length
15
+ end
16
+ end
17
+
18
+ def new_length_hash
19
+ HEADERS.inject({}) do |column_length, header|
20
+ column_length[header.to_sym] = header.length
21
+ column_length
22
+ end
23
+ end
24
+
25
+ def queue_print(queue)
26
+ if queue.empty?
27
+ puts "Nothing in queue!"
28
+ else
29
+ columns_length = find_column_lengths(queue)
30
+ print_headers(columns_length)
31
+ print_contents(columns_length, queue)
32
+ end
33
+ end
34
+
35
+ def print_headers(columns_length)
36
+ HEADERS.each do |header|
37
+ column_width = columns_length[header.to_sym]
38
+ print "#{header.gsub("_", " ").upcase.ljust(column_width)} "
39
+ end
40
+ print "\n"
41
+ end
42
+
43
+ def print_contents(columns_length, queue)
44
+ queue.each_with_index do |attendee, i|
45
+ i+=1
46
+ HEADERS.each do |header|
47
+ column_width = columns_length[header.to_sym]
48
+ print "#{attendee[header].ljust(column_width)} "
49
+ end
50
+ print "\n"
51
+ print_status(i, queue)
52
+ end
53
+ end
54
+
55
+ def print_status(i, queue)
56
+ if i % 10 == 0 && i != 0
57
+ puts "Showing Matches #{i-9}-#{i} of #{queue.length}"
58
+ AcceptAnyInput.read_char
59
+ end
60
+ end
61
+
62
+ def queue_print_by(attribute, queue)
63
+ if attribute.nil? || !queue.first.respond_to?(attribute)
64
+ throw(:top, "Attribute not found to print by...")
65
+ end
66
+
67
+ queue.sort! do |attendee1, attendee2|
68
+ attendee1[attribute] <=> attendee2[attribute]
69
+ end
70
+
71
+ queue_print(queue)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,79 @@
1
+ require 'event_reporter/parse_input'
2
+ require 'event_reporter/database'
3
+ require 'event_reporter/print'
4
+ require 'event_reporter/save'
5
+ require 'event_reporter/csv_parser'
6
+ require 'event_reporter/cleaners'
7
+ require 'event_reporter/attendee'
8
+ require 'event_reporter/cache'
9
+ require 'event_reporter/accept_any_input'
10
+
11
+ require 'CSV'
12
+ require 'yaml'
13
+ require 'json'
14
+ require 'nokogiri'
15
+
16
+ module EventReporter
17
+
18
+ HEADERS = ["last_name", "first_name", "email",
19
+ "zipcode", "city", "state", "address", "phone_number"]
20
+
21
+ class Runner
22
+ include ParseInput
23
+
24
+ def initialize
25
+ puts "EventManager initializing..."
26
+ @database = Database.new
27
+ run_loop
28
+ end
29
+
30
+ private
31
+
32
+ def run_loop
33
+ loop do
34
+ message = catch(:top) do
35
+ route_input(user_input)
36
+ end
37
+ puts message unless message.nil? || !message.kind_of?(String)
38
+ end
39
+ end
40
+
41
+ def user_input
42
+ print "> "
43
+ return gets.chomp.downcase.split(" ")
44
+ end
45
+
46
+ def route_input(input)
47
+ command, leftovers = parse_input(input)
48
+ case command
49
+ when 'quit' then puts "Ciao, bella!\n\n"; exit
50
+ when 'help' then route_help(leftovers)
51
+ when 'load' then @database.load_csv(leftovers)
52
+ when 'find' then @database.route_find('all', leftovers)
53
+ when 'queue' then @database.route_queue(leftovers)
54
+ when 'subtract' then @database.subtract(leftovers)
55
+ when 'add' then @database.add(leftovers)
56
+ else
57
+ throw(:top, "Unknown input. Try 'help'")
58
+ end
59
+ end
60
+
61
+ def route_help(input)
62
+ help_contents = load_help_text
63
+ if input.empty?
64
+ help_contents.each { |key, value| puts value }
65
+ else
66
+ type = input.join("_")
67
+ if help_contents.include? type
68
+ puts help_contents[type]
69
+ else
70
+ throw(:top, "Unknown help command '#{input.join(" ")}'")
71
+ end
72
+ end
73
+ end
74
+
75
+ def load_help_text
76
+ YAML.load_file('bin/help.yml')
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,102 @@
1
+ module EventReporter
2
+ module Save
3
+ class << self
4
+ def route_save_to(filename, queue)
5
+ throw(:top, "Please enter a filename to save to") if filename.nil?
6
+
7
+ extension = filename.split(".")[1]
8
+ method = "save_to_#{extension}"
9
+
10
+ if respond_to? method
11
+ send(method, filename, queue)
12
+ else
13
+ throw(:top, "Extension not valid! (try .csv, .xml, .txt, .json)")
14
+ end
15
+ end
16
+
17
+ def save_to_csv(filename, queue)
18
+ CSV.open("bin/#{filename}", "w") do |csv|
19
+ csv << HEADERS.map do |header|
20
+ header.downcase == 'phone_number' ? 'HomePhone' : header
21
+ end
22
+ queue.each do |attendee|
23
+ csv << [attendee.last_name,
24
+ attendee.first_name,
25
+ attendee.email,
26
+ attendee.zipcode,
27
+ attendee.city,
28
+ attendee.state,
29
+ attendee.address,
30
+ attendee.phone_number]
31
+ end
32
+ end
33
+ success(filename)
34
+ end
35
+
36
+ def save_to_txt(filename, queue)
37
+ columns_length = Print.find_column_lengths(queue)
38
+ File.open("bin/#{filename}", "w") do |txt|
39
+ save_to_txt_headers(txt, columns_length)
40
+ save_to_txt_contents(txt, columns_length, queue)
41
+ end
42
+ success(filename)
43
+ end
44
+
45
+ def save_to_txt_headers(txt, columns_length)
46
+ HEADERS.each do |header|
47
+ column_width = columns_length[header.to_sym]
48
+ txt.print "#{header.gsub("_", " ").upcase.ljust(column_width)} "
49
+ end
50
+ end
51
+
52
+ def save_to_txt_contents(txt, columns_length, queue)
53
+ queue.each_with_index do |attendee, i|
54
+ HEADERS.each do |header|
55
+ column_width = columns_length[header.to_sym]
56
+ txt.print "#{attendee[header].ljust(column_width)} "
57
+ end
58
+ end
59
+ end
60
+
61
+ def save_to_json(filename, queue)
62
+ File.open("bin/#{filename}", "w") do |json|
63
+ json.puts queue.to_json
64
+ end
65
+ success(filename)
66
+ end
67
+
68
+ def save_to_xml(filename, queue)
69
+ queue_xml = Nokogiri::XML::Builder.new do |xml|
70
+ xml.root {
71
+ xml.event_attendees {
72
+ build_attendees_xml(xml, queue)
73
+ }
74
+ }
75
+ end
76
+ File.open("bin/#{filename}", "w") do |xml|
77
+ xml.puts queue_xml.to_xml
78
+ end
79
+ success(filename)
80
+ end
81
+
82
+ def success(filename)
83
+ puts "Saved to #{filename}"
84
+ end
85
+
86
+ def build_attendees_xml(xml, queue)
87
+ queue.each do |attendee|
88
+ xml.attendee {
89
+ xml.first_name attendee.first_name
90
+ xml.last_name attendee.last_name
91
+ xml.email attendee.email
92
+ xml.phone_number attendee.phone_number
93
+ xml.address attendee.address
94
+ xml.city attendee.city
95
+ xml.state attendee.state
96
+ xml.zipcode attendee.zipcode
97
+ }
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: event_reporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Raphael Weiner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '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: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
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'
46
+ description: ! "\n Allows for inspection, filtering, printing and saving of csv
47
+ containing\n list of event attendees through CLI.\n "
48
+ email: raphael.weiner@gmail.com
49
+ executables:
50
+ - event_reporter
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - bin/event_attendees.csv
55
+ - bin/event_reporter
56
+ - bin/help.yml
57
+ - lib/event_reporter/accept_any_input.rb
58
+ - lib/event_reporter/attendee.rb
59
+ - lib/event_reporter/cache.rb
60
+ - lib/event_reporter/cleaners.rb
61
+ - lib/event_reporter/csv_parser.rb
62
+ - lib/event_reporter/database.rb
63
+ - lib/event_reporter/parse_input.rb
64
+ - lib/event_reporter/print.rb
65
+ - lib/event_reporter/runner.rb
66
+ - lib/event_reporter/save.rb
67
+ - README.md
68
+ - LICENSE
69
+ - event_reporter.gemspec
70
+ homepage: http://github.com/raphweiner/event_reporter
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '1.9'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.24
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: Loads csv of event attendees and allows for searching
94
+ test_files: []