conference_tracker 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7286832479c6601a446e51d0ccb8b2cab5bce276
4
+ data.tar.gz: f76f784f392022aabd19e2a7ac0f4a52dcbc9479
5
+ SHA512:
6
+ metadata.gz: 0602d0efcc7c77d339becd15bb1fba3563240ec61e516cf7625612db03169d306499a65270fa4aade8e4b668f3e03ab5391725f1945bc348e46d087b66d67ba3
7
+ data.tar.gz: 5b1ac9dd0f19f1bbd4808b98dd8ed2479c565db77514538d9bf2cb7e1836bb39358055a6632b3f045196cf8cf00c2b41bd5e503a85badd0814260e8d0caf1888
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /*.gem
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in conference_tracker.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,112 @@
1
+ ## Table of Contents
2
+ * [Scope](#scope)
3
+ * [Design](#design)
4
+ * [SRP](#srp)
5
+ * [Installation](#installation)
6
+ * [Usage](#usage)
7
+ * [Library](#library)
8
+ * [CLI](#cli)
9
+
10
+ ## Scope
11
+ This gem is the Ruby implementation of the `Conference Track Management` code-kata.
12
+
13
+ ## Design
14
+
15
+ ### SRP
16
+ The code design follows the single responsibility principle by using a dedicated class for any specific task.
17
+
18
+ ## Installation
19
+ Install the gem from your shell:
20
+ ```shell
21
+ gem install conference_tracker
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Library
27
+ Just require the library into your program and pass an array of notes to the converter:
28
+ ```ruby
29
+ require "conference_tracker"
30
+
31
+ talks = ["Writing Fast Tests Against Enterprise Rails 60min", "Overdoing it in Python 45min", "Lua for the Masses 30min", "Ruby Errors from Mismatched Gem Versions 45min", "Common Ruby Errors 45min"]
32
+
33
+ puts ConferenceTracker.call(talks)
34
+ # Track 1:
35
+ # 09:00AM Writing Fast Tests Against Enterprise Rails 60min
36
+ # 10:00AM Overdoing it in Python 45min
37
+ # 10:45AM Ruby Errors from Mismatched Gem Versions 45min
38
+ # 11:30AM Lua for the Masses 30min
39
+ # 12:00PM Lunch
40
+ # 01:00PM Common Ruby Errors 45min
41
+ # 04:00PM Networking Event
42
+ ```
43
+
44
+ ### CLI
45
+ The gem provides a CLI interface.
46
+ Once installed you will be able to use the `conference_tracker` command from the terminal.
47
+
48
+ #### Help
49
+ You can print CLI help by:
50
+ ```shell
51
+ conference_tracker -h
52
+ Usage: conference_tracker ~/talks.txt
53
+ -h --help Print this help
54
+ <path-to-file> Load conference talks
55
+ ```
56
+
57
+ #### Input
58
+ The program accepts as input a file containing the talks titles with their duration:
59
+ ```txt
60
+ # ~/talks.txt
61
+ Writing Fast Tests Against Enterprise Rails 60min
62
+ Overdoing it in Python 45min
63
+ Lua for the Masses 30min
64
+ Ruby Errors from Mismatched Gem Versions 45min
65
+ Common Ruby Errors 45min
66
+ Rails for Python Developers lightning
67
+ Communicating Over Distance 60min
68
+ Accounting-Driven Development 45min
69
+ Woah 30min
70
+ Sit Down and Write 30min
71
+ Pair Programming vs Noise 45min
72
+ Rails Magic 60min
73
+ Ruby on Rails: Why We Should Move On 60min
74
+ Clojure Ate Scala (on my project) 45min
75
+ Programming in the Boondocks of Seattle 30min
76
+ Ruby vs. Clojure for Back-End Development 30min
77
+ Ruby on Rails Legacy App Maintenance 60min
78
+ A World Without HackerNews 30min
79
+ User Interface CSS in Rails Apps 30min
80
+ ```
81
+
82
+ Just pass the file path to the program:
83
+ ```shell
84
+ conference_tracker ~/talks.txt
85
+ > Track 1:
86
+ > 09:00AM Rails Magic 60min
87
+ > 10:00AM Ruby on Rails: Why We Should Move On 60min
88
+ > 11:00AM Writing Fast Tests Against Enterprise Rails 60min
89
+ > 12:00PM Lunch
90
+ > 01:00PM Ruby on Rails Legacy App Maintenance 60min
91
+ > 02:00PM Communicating Over Distance 60min
92
+ > 03:00PM Ruby Errors from Mismatched Gem Versions 45min
93
+ > 03:45PM Pair Programming vs Noise 45min
94
+ > 04:30PM User Interface CSS in Rails Apps 30min
95
+ > 05:00PM Networking Event
96
+
97
+ > Track 2:
98
+ > 09:00AM Accounting-Driven Development 45min
99
+ > 09:45AM Clojure Ate Scala (on my project) 45min
100
+ > 10:30AM Common Ruby Errors 45min
101
+ > 11:15AM Overdoing it in Python 45min
102
+ > 12:00PM Lunch
103
+ > 01:00PM Lua for the Masses 30min
104
+ > 01:30PM Woah 30min
105
+ > 02:00PM Sit Down and Write 30min
106
+ > 02:30PM Programming in the Boondocks of Seattle 30min
107
+ > 03:00PM Ruby vs. Clojure for Back-End Development 30min
108
+ > 03:30PM A World Without HackerNews 30min
109
+ > 04:00PM Rails for Python Developers lightning
110
+ > 04:05PM Networking Event
111
+
112
+ ```
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:spec) do |t|
5
+ t.libs << "spec"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["spec/**/*_spec.rb"]
8
+ end
9
+
10
+ Rake::TestTask.new(:bench) do |t|
11
+ t.libs << "spec"
12
+ t.libs << "lib"
13
+ t.test_files = FileList["spec/**/*_bench.rb"]
14
+ end
15
+
16
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path("../../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require "conference_tracker"
7
+
8
+ input = ARGV.fetch(0, "")
9
+ ConferenceTracker::CLI.new(input).call
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "conference_tracker"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "conference_tracker/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "conference_tracker"
8
+ s.version = ConferenceTracker::VERSION
9
+ s.authors = ["costajob"]
10
+ s.email = ["costajob@gmail.com"]
11
+ s.summary = "Implementation of the Conference Track Management kata"
12
+ s.homepage = "https://bitbucket.org/costajob/conference_tracker"
13
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
14
+ s.bindir = "bin"
15
+ s.executables << "conference_tracker"
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency "bundler", "~> 1.15"
19
+ s.add_development_dependency "rake", "~> 10.0"
20
+ s.add_development_dependency "minitest", "~> 5.0"
21
+ s.add_development_dependency "benchmark-ips", "~> 2"
22
+ end
@@ -0,0 +1,43 @@
1
+ require "conference_tracker/scheduler"
2
+
3
+ module ConferenceTracker
4
+ class CLI
5
+ HELP_FLAGS = %w[-h --help]
6
+ COL_WIDTH = 23
7
+
8
+ def initialize(input,
9
+ pipe = STDOUT,
10
+ scheduler = Scheduler)
11
+ @input = input
12
+ @pipe = pipe
13
+ @scheduler = scheduler
14
+ end
15
+
16
+ def call
17
+ @pipe.puts output
18
+ end
19
+
20
+ private def output
21
+ return help if help?
22
+ return unless file?
23
+ talks = File.readlines(@input).map(&:strip)
24
+ @scheduler.new(talks).call
25
+ end
26
+
27
+ private def file?
28
+ File.file?(File.expand_path(@input))
29
+ end
30
+
31
+ private def help?
32
+ HELP_FLAGS.include?(@input)
33
+ end
34
+
35
+ private def help
36
+ [].tap do |h|
37
+ h << %q{Usage: conference_tracker ~/talks.txt}
38
+ h << " %-#{COL_WIDTH}s Print this help" % "-h --help"
39
+ h << " %-#{COL_WIDTH}s Load conference talks" % "<path-to-file>"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ module ConferenceTracker
2
+ class Duration
3
+ SECS_IN_MIN = 60
4
+ UNITS = {
5
+ "lightning" => 5
6
+ }
7
+
8
+ def initialize(value)
9
+ @value = UNITS.fetch(value, value).to_i
10
+ end
11
+
12
+ def to_i
13
+ @value * SECS_IN_MIN
14
+ end
15
+
16
+ def to_s
17
+ UNITS.invert.fetch(@value, "#{@value}min")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ module ConferenceTracker
2
+ class Event
3
+ NETWORKING = "Networking Event"
4
+
5
+ class Constraint
6
+ attr_reader :early_start
7
+
8
+ def initialize(early_start)
9
+ @early_start = Time.parse(early_start)
10
+ end
11
+
12
+ def valid?(time)
13
+ time >= @early_start
14
+ end
15
+ end
16
+
17
+ def self.lunch
18
+ new("Lunch", "12PM")
19
+ end
20
+
21
+ def self.networking
22
+ new("Networking Event", "4PM", Constraint.new("4PM"))
23
+ end
24
+
25
+ def initialize(name, start, constraint = nil)
26
+ @name = name
27
+ @start = ConferenceTracker.parse(start)
28
+ @constraint = constraint
29
+ end
30
+
31
+ def to_s
32
+ "#{early_start.strftime(FORMAT)} #{@name}"
33
+ end
34
+
35
+ def update(start)
36
+ @start = ConferenceTracker.parse(start)
37
+ self
38
+ end
39
+
40
+ private def early_start
41
+ return @start if @constraint.nil? || @constraint.valid?(@start)
42
+ @constraint.early_start
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ require "conference_tracker/talk"
2
+ require "conference_tracker/track"
3
+
4
+ module ConferenceTracker
5
+ class Scheduler
6
+ TRACK = "Track"
7
+
8
+ def initialize(talks, talk = Talk, track = Track)
9
+ @talks = talk.bulk(talks.to_a).sort { |a,b| b.to_i <=> a.to_i }
10
+ @track = track
11
+ end
12
+
13
+ def call
14
+ collect.to_a.reject(&:empty?).each_with_index.map do |track, i|
15
+ track.name = "#{TRACK} #{i+1}:"
16
+ track.schedule << ""
17
+ end
18
+ end
19
+
20
+ private def collect
21
+ return if @talks.empty?
22
+ @talks.reduce([]) do |tracks, talk|
23
+ tracks.each { |track| track.add(talk) }
24
+ tracks << @track.new.add(talk) unless talk.scheduled?
25
+ tracks
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ require "forwardable"
2
+
3
+ module ConferenceTracker
4
+ class Session
5
+ extend Forwardable
6
+
7
+ def self.morning
8
+ new("9am", "12pm")
9
+ end
10
+
11
+ def self.afternoon
12
+ new("1pm", "5pm")
13
+ end
14
+
15
+ def_delegators :@talks, :size, :empty?
16
+
17
+ attr_reader :next_start
18
+
19
+ def initialize(start, finish)
20
+ @start = ConferenceTracker.parse(start)
21
+ @finish = ConferenceTracker.parse(finish)
22
+ @lapse = @finish - @start
23
+ @talks = []
24
+ end
25
+
26
+ def schedule
27
+ @talks.map(&:to_s)
28
+ end
29
+
30
+ def add(talk)
31
+ return if talk.scheduled? || overflow?(talk)
32
+ @talks << talk.schedule(next_start)
33
+ end
34
+
35
+ def next_start
36
+ @start + length
37
+ end
38
+
39
+ private def overflow?(talk)
40
+ length + talk.to_i > @lapse
41
+ end
42
+
43
+ private def length
44
+ @talks.reduce(0) { |length, talk| length += talk.to_i }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ require "conference_tracker/duration"
2
+
3
+ module ConferenceTracker
4
+ class Talk
5
+ RULE = /(.*) (\d+min|#{Duration::UNITS.keys.join("|")})/
6
+
7
+ def self.factory(talk)
8
+ matching = talk.match(RULE)
9
+ return unless matching
10
+ new(*matching.captures)
11
+ end
12
+
13
+ def self.bulk(talks)
14
+ talks.map { |talk| factory(talk) }.compact
15
+ end
16
+
17
+ def initialize(name, duration)
18
+ @name = name
19
+ @duration = Duration.new(duration)
20
+ end
21
+
22
+ def to_s
23
+ [].tap do |s|
24
+ s << @start.strftime(FORMAT) if @start
25
+ s << @name
26
+ s << @duration
27
+ end.compact.join(" ")
28
+ end
29
+
30
+ def to_i
31
+ @duration.to_i
32
+ end
33
+
34
+ def schedule(time)
35
+ @start = ConferenceTracker.parse(time)
36
+ self
37
+ end
38
+
39
+ def scheduled?
40
+ !!@start
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,34 @@
1
+ require "conference_tracker/event"
2
+ require "conference_tracker/session"
3
+
4
+ module ConferenceTracker
5
+ class Track
6
+ attr_accessor :name
7
+ attr_reader :morning, :afternoon
8
+
9
+ def initialize(morning = Session.morning, afternoon = Session.afternoon, event = Event)
10
+ @morning = morning
11
+ @afternoon = afternoon
12
+ @event = event
13
+ end
14
+
15
+ def add(talk)
16
+ @morning.add(talk) || @afternoon.add(talk)
17
+ self
18
+ end
19
+
20
+ def empty?
21
+ @morning.empty?
22
+ end
23
+
24
+ def schedule
25
+ [].tap do |s|
26
+ s << @name
27
+ s.concat(@morning.schedule)
28
+ s << @event.lunch.to_s
29
+ s.concat(@afternoon.schedule)
30
+ s << @event.networking.update(@afternoon.next_start).to_s
31
+ end.compact
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module ConferenceTracker
2
+ VERSION = "1.1.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ require "time"
2
+ require "conference_tracker/cli"
3
+ require "conference_tracker/version"
4
+
5
+ module ConferenceTracker
6
+ extend self
7
+
8
+ FORMAT = "%I:%M%p"
9
+
10
+ def call(talks)
11
+ Scheduler.new(talks).call
12
+ end
13
+
14
+ def parse(time)
15
+ return time if time.instance_of?(Time)
16
+ Time.parse(time)
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: conference_tracker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - costajob
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: benchmark-ips
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
69
+ description:
70
+ email:
71
+ - costajob@gmail.com
72
+ executables:
73
+ - conference_tracker
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - bin/conference_tracker
82
+ - bin/console
83
+ - bin/setup
84
+ - conference_tracker.gemspec
85
+ - lib/conference_tracker.rb
86
+ - lib/conference_tracker/cli.rb
87
+ - lib/conference_tracker/duration.rb
88
+ - lib/conference_tracker/event.rb
89
+ - lib/conference_tracker/scheduler.rb
90
+ - lib/conference_tracker/session.rb
91
+ - lib/conference_tracker/talk.rb
92
+ - lib/conference_tracker/track.rb
93
+ - lib/conference_tracker/version.rb
94
+ homepage: https://bitbucket.org/costajob/conference_tracker
95
+ licenses: []
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.6.11
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Implementation of the Conference Track Management kata
117
+ test_files: []