network_executive 0.0.4 → 0.0.7

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.
Files changed (92) hide show
  1. data/README.md +53 -24
  2. data/app/assets/fonts/network_executive/league_gothic.css +13 -0
  3. data/app/assets/fonts/network_executive/league_gothic.svg +148 -0
  4. data/app/assets/fonts/network_executive/league_gothic.ttf +0 -0
  5. data/app/assets/fonts/network_executive/league_gothic.woff +0 -0
  6. data/app/assets/javascripts/network_executive/components/iframe_notifier.coffee +14 -0
  7. data/app/assets/javascripts/network_executive/components/namespace.coffee +1 -0
  8. data/app/assets/javascripts/network_executive/components/osd.coffee +65 -0
  9. data/app/assets/javascripts/network_executive/components/photo_player.coffee +55 -0
  10. data/app/assets/javascripts/network_executive/components/postman.coffee +23 -0
  11. data/app/assets/javascripts/network_executive/components/set_top_box.coffee +81 -0
  12. data/app/assets/javascripts/network_executive/components/tweet_player.coffee +95 -0
  13. data/app/assets/javascripts/network_executive/components/you_tube_player.coffee +85 -0
  14. data/app/assets/javascripts/network_executive/slideshow.coffee +6 -0
  15. data/app/assets/javascripts/network_executive/twitter.coffee +7 -0
  16. data/app/assets/javascripts/network_executive/you_tube.coffee +8 -0
  17. data/app/assets/javascripts/network_executive.coffee +10 -0
  18. data/app/assets/stylesheets/network_executive/{gui_components.css → components/gui_components.css} +0 -0
  19. data/app/assets/stylesheets/network_executive/{normalize.css → components/normalize.css} +0 -0
  20. data/app/assets/stylesheets/network_executive/{osd.css → components/osd.css} +6 -34
  21. data/app/assets/stylesheets/network_executive/components/osd_guide.css +43 -0
  22. data/app/assets/stylesheets/network_executive/components/photo_player.css +61 -0
  23. data/app/assets/stylesheets/network_executive/{smpte.css → components/smpte.css} +0 -0
  24. data/app/assets/stylesheets/network_executive/components/tweet_player.css +60 -0
  25. data/app/assets/stylesheets/network_executive/components/you_tube_player.css +12 -0
  26. data/app/assets/stylesheets/network_executive/off_air.css +5 -0
  27. data/app/assets/stylesheets/network_executive/slideshow.css +4 -0
  28. data/app/assets/stylesheets/network_executive/twitter.css +5 -0
  29. data/app/assets/stylesheets/network_executive/you_tube.css +3 -0
  30. data/app/assets/stylesheets/{network_executive/application.css → network_executive.css} +5 -5
  31. data/app/controllers/network_executive/components_controller.rb +4 -0
  32. data/app/controllers/network_executive/guide_controller.rb +41 -0
  33. data/app/controllers/network_executive/programs_controller.rb +9 -0
  34. data/app/helpers/network_executive/network_helper.rb +6 -0
  35. data/app/models/network_executive/channel.rb +15 -14
  36. data/app/models/network_executive/guide.rb +65 -0
  37. data/app/models/network_executive/program.rb +69 -5
  38. data/app/models/network_executive/viewer.rb +4 -0
  39. data/app/programs/network_executive/off_air.rb +18 -0
  40. data/app/views/network_executive/components/slideshow.html.erb +20 -0
  41. data/app/views/network_executive/components/twitter.html.erb +16 -0
  42. data/app/views/network_executive/components/you_tube.html.erb +13 -0
  43. data/app/views/network_executive/guide/index.html.erb +30 -0
  44. data/app/views/network_executive/network/index.html.erb +3 -3
  45. data/app/views/network_executive/programs/off_air.html.erb +13 -0
  46. data/config/routes.rb +7 -1
  47. data/lib/network_executive/channel_schedule.rb +65 -0
  48. data/lib/network_executive/components/photo_player.rb +48 -0
  49. data/lib/network_executive/components/tweet_player.rb +48 -0
  50. data/lib/network_executive/components/you_tube_player.rb +15 -0
  51. data/lib/network_executive/components.rb +3 -0
  52. data/lib/network_executive/engine.rb +29 -3
  53. data/lib/network_executive/off_air_schedule.rb +18 -0
  54. data/lib/network_executive/producer.rb +3 -3
  55. data/lib/network_executive/program_schedule.rb +96 -0
  56. data/lib/network_executive/program_schedule_proxy.rb +55 -0
  57. data/lib/network_executive/scheduled_program.rb +26 -0
  58. data/lib/network_executive/scheduling.rb +15 -21
  59. data/lib/network_executive/time_calculations.rb +10 -0
  60. data/lib/network_executive/version.rb +1 -1
  61. data/lib/network_executive.rb +2 -1
  62. data/network_executive.gemspec +5 -0
  63. data/spec/models/channel_spec.rb +30 -12
  64. data/spec/models/{lineup_spec.rb → guide_spec.rb} +28 -6
  65. data/spec/models/program_spec.rb +46 -3
  66. data/spec/models/viewer_spec.rb +9 -1
  67. data/spec/programs/off_air_spec.rb +9 -0
  68. data/spec/spec_helper.rb +3 -2
  69. data/spec/unit/channel_schedule_spec.rb +67 -0
  70. data/spec/unit/components/photo_player_spec.rb +76 -0
  71. data/spec/unit/components/tweet_player_spec.rb +68 -0
  72. data/spec/unit/components/you_tube_player_spec.rb +17 -0
  73. data/spec/unit/off_air_schedule_spec.rb +28 -0
  74. data/spec/unit/producer_spec.rb +2 -4
  75. data/spec/unit/program_schedule_proxy_spec.rb +102 -0
  76. data/spec/unit/program_schedule_spec.rb +191 -0
  77. data/spec/unit/scheduled_program_spec.rb +57 -0
  78. data/spec/unit/scheduling_spec.rb +61 -72
  79. data/vendor/assets/javascripts/bigtext.js +264 -0
  80. metadata +185 -44
  81. data/app/assets/javascripts/application.js +0 -13
  82. data/app/assets/javascripts/network_executive/osd.js +0 -62
  83. data/app/assets/javascripts/network_executive/set_top_box.js +0 -45
  84. data/app/controllers/network_executive/lineup_controller.rb +0 -11
  85. data/app/models/network_executive/channel_schedule.rb +0 -13
  86. data/app/models/network_executive/lineup.rb +0 -74
  87. data/app/models/network_executive/lineup_range.rb +0 -34
  88. data/app/models/network_executive/program_schedule.rb +0 -148
  89. data/app/views/network_executive/lineup/index.html.erb +0 -26
  90. data/spec/models/channel_schedule_spec.rb +0 -29
  91. data/spec/models/lineup_range_spec.rb +0 -65
  92. data/spec/models/program_schedule_spec.rb +0 -399
@@ -33,6 +33,10 @@ module NetworkExecutive
33
33
  self.id = channel.subscribe { |msg| stream.send msg }
34
34
 
35
35
  stream.onclose = method(:tune_out).to_proc
36
+
37
+ channel.play_whats_on do |msg|
38
+ stream.send msg
39
+ end
36
40
  end
37
41
 
38
42
  def tune_out( event )
@@ -0,0 +1,18 @@
1
+ # Acts as a NullObject when there is nothing scheduled for a given channel
2
+ module NetworkExecutive
3
+ class OffAir < NetworkExecutive::Program
4
+
5
+ def display_name
6
+ 'Off Air'
7
+ end
8
+
9
+ def url
10
+ NetworkExecutive::Engine.routes.url_helpers.program_path 'off_air'
11
+ end
12
+
13
+ def duration
14
+ Guide::Interval.minutes
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= stylesheet_link_tag 'network_executive/slideshow' %>
5
+ <link href='//fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
6
+ </head>
7
+ <body>
8
+ <figure id="photo_player" class="photo-player">
9
+ <img id="current_photo" class="current-photo">
10
+ <figcaption id="caption" class="caption">
11
+ <div id="title" class="title"></div>
12
+ <div id="description" class="desc"></div>
13
+ <div id="photographer" class="photog"></div>
14
+ <div id="location" class="location"></div>
15
+ </figcaption>
16
+ </figure>
17
+
18
+ <%= javascript_include_tag 'network_executive/slideshow' %>
19
+ </body>
20
+ </html>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= stylesheet_link_tag 'network_executive/twitter' %>
5
+ </head>
6
+ <body>
7
+ <img id="background" class="background">
8
+ <div id="tweet" class="tweet">
9
+ <div class="tweet-text"></div>
10
+ <div id="author" class="author"></div>
11
+ </div>
12
+
13
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
14
+ <%= javascript_include_tag 'network_executive/twitter' %>
15
+ </body>
16
+ </html>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= stylesheet_link_tag 'network_executive/you_tube' %>
5
+ <%= javascript_include_tag 'network_executive/you_tube' %>
6
+ <%= javascript_include_tag '//www.youtube.com/iframe_api' %>
7
+ <script>
8
+ </script>
9
+ </head>
10
+ <body>
11
+ <div id="player"></div>
12
+ </body>
13
+ </html>
@@ -0,0 +1,30 @@
1
+ <table>
2
+ <thead>
3
+ <tr>
4
+ <td style="width: <%= guide_header_width %>%;">&nbsp;</td>
5
+ <% @guide.times.each do |t| %>
6
+ <th class="guide-time" style="width: <%= guide_header_width %>%;">
7
+ <%= t.strftime '%l:%M' %>
8
+ </th>
9
+ <% end %>
10
+ </tr>
11
+ </thead>
12
+ <tbody>
13
+ <% @guide[:channels].each do |ch| %>
14
+ <tr>
15
+ <th class="guide-channel">
16
+ <%= link_to ch[:channel].display_name, channel_path( ch[:channel] ) %>
17
+ </th>
18
+ <td colspan="<%= @guide.times.size %>">
19
+ <% ch[:programs].each do |program| %>
20
+ <div class="guide-program-ct" style="width: <%= '%.3f' % program.portion %>%;">
21
+ <span class="guide-program">
22
+ <%= program.display_name %>
23
+ </span>
24
+ </div>
25
+ <% end %>
26
+ </td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width">
6
6
  <title>Network Executive: <%= network_name %></title>
7
7
  <link href='//fonts.googleapis.com/css?family=Lekton:400,700|Quicksand:300,400,700' rel='stylesheet' type='text/css'>
8
- <%= stylesheet_link_tag 'network_executive/application', media:'all' %>
8
+ <%= stylesheet_link_tag 'network_executive', media:'all' %>
9
9
  <base href="<%= network_executive.root_url %>"/>
10
10
  </head>
11
11
  <body>
@@ -28,9 +28,9 @@
28
28
  </aside>
29
29
  </h1>
30
30
  </header>
31
- <article id="lineup"></article>
31
+ <article id="guide" class="guide"></article>
32
32
  </div>
33
33
 
34
- <%= javascript_include_tag 'application' %>
34
+ <%= javascript_include_tag 'network_executive' %>
35
35
  </body>
36
36
  </html>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= stylesheet_link_tag 'network_executive/off_air', media:'all' %>
5
+ <link href='//fonts.googleapis.com/css?family=Lekton:400,700|Quicksand:300,400,700' rel='stylesheet' type='text/css'>
6
+ </head>
7
+ <body>
8
+ <div id="smpte">
9
+ <h1 id="smpte_message" class="msg">Off Air</h1>
10
+ <aside class="network-name"><%= network_name %></aside>
11
+ </div>
12
+ </body>
13
+ </html>
data/config/routes.rb CHANGED
@@ -3,7 +3,13 @@ NetworkExecutive::Engine.routes.draw do
3
3
 
4
4
  match 'channels/:channel_name' => 'network#index', as: :channel
5
5
 
6
- match 'lineup' => 'lineup#index', as: :lineup
6
+ match 'you_tube' => 'components#you_tube', as: :you_tube
7
+ match 'twitter' => 'components#twitter', as: :twitter
8
+ match 'slideshow' => 'components#slideshow', as: :slideshow
9
+
10
+ match 'guide' => 'guide#index', as: :guide
11
+
12
+ match 'programs/:program_name' => 'programs#show', as: :program
7
13
 
8
14
  root to:'network#index'
9
15
  end
@@ -0,0 +1,65 @@
1
+ require 'network_executive/program_schedule'
2
+ require 'network_executive/off_air_schedule'
3
+ require 'network_executive/scheduled_program'
4
+
5
+ module NetworkExecutive
6
+ class ChannelSchedule < Array
7
+
8
+ def add( program, options = {}, &block )
9
+ unshift ProgramSchedule.new( program, options, &block )
10
+ end
11
+
12
+ def whats_on_between?( start, stop, interval )
13
+ cursor = start.dup
14
+ programs = []
15
+
16
+ until cursor > stop do
17
+ program = find_program_occurring_at( cursor )
18
+
19
+ occurrence = program.occurrence_at cursor
20
+
21
+ if repeated_off_air?( programs, program )
22
+ program, occurrence, remainder = extend_off_air( programs, program, stop )
23
+ else
24
+ remainder = time_left( cursor, occurrence.end_time, stop )
25
+ end
26
+
27
+ portion = remainder / (stop - start) * 100
28
+ step = occurrence.end_time - cursor
29
+
30
+ programs << ScheduledProgram.new( program, occurrence, remainder, portion )
31
+
32
+ cursor += step + 1
33
+ end
34
+
35
+ programs
36
+ end
37
+
38
+ private
39
+
40
+ def find_program_occurring_at( time )
41
+ find { |p| p.occurring_at? time } || OffAirSchedule.new( start_time:time )
42
+ end
43
+
44
+ def time_left( time, end_time, max = end_time )
45
+ [ max, end_time ].min - time
46
+ end
47
+
48
+ def repeated_off_air?( programs, program )
49
+ return unless last = programs.last
50
+
51
+ last.program.is_a?( OffAirSchedule ) && program.is_a?( OffAirSchedule )
52
+ end
53
+
54
+ # Extends the previous Off Air schedule
55
+ def extend_off_air( programs, program, stop )
56
+ prev = programs.pop + program
57
+ program = prev.program
58
+ occurrence = prev.occurrence
59
+ remainder = time_left( occurrence.start_time, occurrence.end_time, stop )
60
+
61
+ [ program, occurrence, remainder ]
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ require 'net/http'
2
+
3
+ module NetworkExecutive
4
+ module Components
5
+ module PhotoPlayer
6
+
7
+ def url
8
+ NetworkExecutive::Engine.routes.url_helpers.slideshow_path
9
+ end
10
+
11
+ def refresh
12
+ false
13
+ end
14
+
15
+ def onload
16
+ {
17
+ photos: photos
18
+ }
19
+ end
20
+
21
+ def feed_url
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def photos
26
+ []
27
+ end
28
+
29
+ def feed
30
+ _url = URI.parse( feed_url )
31
+ http = Net::HTTP.new( _url.host, _url.port )
32
+
33
+ if _url.scheme == 'https'
34
+ http.use_ssl = true
35
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
36
+ end
37
+
38
+ request = Net::HTTP::Get.new( _url.request_uri )
39
+ response = http.request( request )
40
+
41
+ JSON.parse response.body
42
+ rescue StandardError
43
+ {}
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,48 @@
1
+ require 'active_support/concern'
2
+
3
+ require 'twitter'
4
+
5
+ # TODO: Refactor this into a plugin. Might want to include a generator that
6
+ # creates an initializer.
7
+ module NetworkExecutive
8
+ module Components
9
+ module TweetPlayer
10
+ extend ActiveSupport::Concern
11
+
12
+ def url
13
+ NetworkExecutive::Engine.routes.url_helpers.twitter_path
14
+ end
15
+
16
+ def refresh
17
+ false
18
+ end
19
+
20
+ def onload
21
+ {
22
+ tweets: self.class.tweets[:statuses]
23
+ }
24
+ end
25
+
26
+ module ClassMethods
27
+ attr_accessor :client, :query
28
+
29
+ def client
30
+ @client ||= Twitter::Client.new
31
+ end
32
+
33
+ def configure
34
+ yield client
35
+ end
36
+
37
+ def search( *args )
38
+ @query = args
39
+ end
40
+
41
+ def tweets
42
+ client.search( *query )
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ module NetworkExecutive
2
+ module Components
3
+ module YouTubePlayer
4
+
5
+ def url
6
+ NetworkExecutive::Engine.routes.url_helpers.you_tube_path
7
+ end
8
+
9
+ def refresh
10
+ false
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ require 'network_executive/components/photo_player'
2
+ require 'network_executive/components/tweet_player'
3
+ require 'network_executive/components/you_tube_player'
@@ -1,5 +1,8 @@
1
1
  # TODO: Only load when needed (enables asset pipeline)
2
2
  require 'sass-rails'
3
+ require 'ice_cube'
4
+
5
+ require 'network_executive/time_calculations'
3
6
 
4
7
  module NetworkExecutive
5
8
 
@@ -8,18 +11,41 @@ module NetworkExecutive
8
11
 
9
12
  isolate_namespace NetworkExecutive
10
13
 
14
+ initializer 'network_executive.extend_time' do |app|
15
+ Time.send :include, TimeCalculations
16
+ end
17
+
18
+ initializer 'network_executive.eager_load_default_programs' do |app|
19
+ eager_load_models_in 'programs', self
20
+ end
21
+
22
+ initializer 'network_executive.eager_load_user_programs' do |app|
23
+ eager_load_models_in 'programs', app
24
+ end
25
+
11
26
  initializer 'network_executive.eager_load_channels' do |app|
12
27
  eager_load_models_in 'channels', app
13
28
  end
14
29
 
15
- initializer 'network_executive.eager_load_programs' do |app|
16
- eager_load_models_in 'programs', app
30
+ initializer :assets do |app|
31
+ app.config.assets.paths << File.join( root, 'app', 'assets', 'fonts' )
32
+
33
+ app.config.assets.precompile += [
34
+ 'network_executive.js',
35
+ 'network_executive/you_tube.js',
36
+ 'network_executive/twitter.js',
37
+
38
+ 'network_executive.css',
39
+ 'network_executive/off_air.css',
40
+ 'network_executive/you_tube.css',
41
+ 'network_executive/twitter.css',
42
+ ]
17
43
  end
18
44
 
19
45
  private
20
46
 
21
47
  def eager_load_models_in( sub_path, app )
22
- Dir[ File.join(app.root, 'app', sub_path, '*') ].each do |c|
48
+ Dir[ File.join(app.root, 'app', sub_path, '**', '*.rb') ].each do |c|
23
49
  require c
24
50
  end
25
51
  end
@@ -0,0 +1,18 @@
1
+ # Acts as a NullObject for ProgramSchedule
2
+ module NetworkExecutive
3
+ class OffAirSchedule < ProgramSchedule
4
+
5
+ def initialize( options = {} )
6
+ options[:duration] ||= 59.seconds
7
+
8
+ super 'off_air', options
9
+ end
10
+
11
+ def occurrence_at( time )
12
+ end_time = time + duration
13
+
14
+ ProgramSchedule::Occurrence.new time, duration, end_time
15
+ end
16
+
17
+ end
18
+ end
@@ -24,10 +24,10 @@ module NetworkExecutive
24
24
  end
25
25
 
26
26
  def run_scheduled_programming
27
+ Rails.logger.info "\n\n"
28
+
27
29
  Network.channels.each do |channel|
28
- if scheduled_program = channel.whats_on?
29
- channel.show scheduled_program
30
- end
30
+ channel.play channel.whats_on?
31
31
  end
32
32
  end
33
33
 
@@ -0,0 +1,96 @@
1
+ require 'network_executive/program_schedule_proxy'
2
+
3
+ module NetworkExecutive
4
+ class ProgramSchedule
5
+ attr_accessor :program, :start_time, :duration, :proxy
6
+
7
+ Occurrence = Struct.new('Occurrence', :start_time, :duration, :end_time)
8
+
9
+ def initialize( program, options = {}, &block )
10
+ options.symbolize_keys!
11
+
12
+ options[:duration] ||= 24.hours
13
+
14
+ @start_time, @duration = options[:start_time], options[:duration]
15
+
16
+ @program = Program.find_by_name program
17
+
18
+ raise ProgramNameError, %Q{"#{program}" is not a registered Program} unless @program
19
+
20
+ @block = block
21
+ end
22
+
23
+ def proxy
24
+ @proxy ||= ProgramScheduleProxy.new( start_time, duration, &@block )
25
+ end
26
+
27
+ def start_time=( time )
28
+ @start_time = time
29
+
30
+ reset_proxy!
31
+
32
+ time
33
+ end
34
+
35
+ def duration=( d )
36
+ @duration = d
37
+
38
+ reset_proxy!
39
+
40
+ d
41
+ end
42
+
43
+ def whats_on?( time = Time.now )
44
+ proxy.occurring_at? time
45
+ end
46
+
47
+ def occurs_at?( time )
48
+ proxy.occurs_at? time
49
+ end
50
+
51
+ def play( &block )
52
+ program.play( &block )
53
+ end
54
+
55
+ def update( &block )
56
+ program.update( &block )
57
+ end
58
+
59
+ # Returns the scheduled occurrence that matches the specified time
60
+ def occurrence_at( time )
61
+ real_duration = duration - 1
62
+
63
+ range = [ time - real_duration, time ]
64
+
65
+ start_time = proxy.occurrences_between( range[0], range[1] ).first
66
+ end_time = start_time + real_duration
67
+
68
+ Occurrence.new start_time, real_duration, end_time
69
+ end
70
+
71
+ def respond_to_missing?( method_id, include_private = false )
72
+ proxy.respond_to?( method_id ) \
73
+ || program.respond_to?( method_id ) \
74
+ || super
75
+ end
76
+
77
+ def method_missing( method_id, *args )
78
+ if program.respond_to?( method_id )
79
+ program.send method_id, *args
80
+ elsif proxy.respond_to?( method_id )
81
+ proxy.send method_id, *args
82
+ else
83
+ super
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def reset_proxy!
90
+ @proxy = nil
91
+
92
+ self.proxy
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,55 @@
1
+ require 'ice_cube'
2
+
3
+ # A proxy object to IceCube::Schedule
4
+ #
5
+ # Mainly, this allows some method missing magic during block evaluation
6
+ # that simplifies the `schedule` DSL by adding new recurrence rules for
7
+ # each Rule method that is called from within the block.
8
+ module NetworkExecutive
9
+ class ProgramScheduleProxy
10
+
11
+ attr_accessor :start_time, :duration
12
+
13
+ def initialize( start_time = nil, duration = nil, &block )
14
+ start_time ||= Time.now.beginning_of_day
15
+ duration ||= 24.hours
16
+
17
+ @start_time, @duration = start_time, duration
18
+
19
+ @schedule = IceCube::Schedule.new( start_time, duration:duration.to_i )
20
+
21
+ if block_given?
22
+ @parsing_schedule = true
23
+
24
+ instance_eval( &block )
25
+
26
+ @parsing_schedule = false
27
+ end
28
+ end
29
+
30
+ def to_schedule
31
+ @schedule
32
+ end
33
+
34
+ def respond_to_missing?( method_id, include_private = false )
35
+ if @parsing_schedule
36
+ IceCube::Rule.respond_to?( method_id ) || super
37
+ else
38
+ @schedule.respond_to?( method_id ) || super
39
+ end
40
+ end
41
+
42
+ def method_missing( method_id, *args )
43
+ if @parsing_schedule && IceCube::Rule.respond_to?( method_id )
44
+ IceCube::Rule.send( method_id, *args ).tap do |rule|
45
+ @schedule.add_recurrence_rule rule
46
+ end
47
+ elsif @schedule.respond_to?( method_id )
48
+ @schedule.send( method_id, *args )
49
+ else
50
+ super
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,26 @@
1
+ module NetworkExecutive
2
+ class ScheduledProgram
3
+ attr_accessor :program, :occurrence, :remainder, :portion
4
+
5
+ def initialize( *args )
6
+ @program, @occurrence, @remainder, @portion = *args
7
+ end
8
+
9
+ def display_name
10
+ program.display_name
11
+ end
12
+
13
+ # Extends this scheduled program with another program of the same type.
14
+ def +( other_program )
15
+ raise ArgumentError if @program.class != other_program.class
16
+
17
+ additional_duration = other_program.duration + 1
18
+
19
+ program.duration += additional_duration
20
+ occurrence.duration += additional_duration
21
+ occurrence.end_time += additional_duration
22
+
23
+ self
24
+ end
25
+ end
26
+ end
@@ -1,29 +1,21 @@
1
1
  require 'active_support/concern'
2
2
 
3
+ require 'network_executive/channel_schedule'
4
+
3
5
  module NetworkExecutive
4
6
  module Scheduling
5
7
  extend ActiveSupport::Concern
6
8
 
7
- def whats_on?( start_time = nil, stop_time = nil, options = {} )
8
- start_time ||= Time.now
9
-
10
- if stop_time
11
- with_showtimes_between( start_time, stop_time, options ) do |showtime, program|
12
- program = (block_given? ? yield(program) : program)
13
-
14
- { time:showtime, program:program }
15
- end
16
- else
17
- schedule.find_by_showtime start_time
18
- end
9
+ def whats_on_at?( time )
10
+ schedule.find { |sch| sch.whats_on? time } || OffAirSchedule.new
19
11
  end
20
12
 
21
- def with_showtimes_between( start, stop, options = {} )
22
- LineupRange.new( start, stop, options ).each_with_object([]) do |showtime, lineup|
23
- program = schedule.find_by_showtime showtime
13
+ def whats_on?
14
+ whats_on_at? Time.now.change(sec:0)
15
+ end
24
16
 
25
- lineup << yield( showtime, program )
26
- end
17
+ def whats_on_between?( start, stop, interval = nil )
18
+ schedule.whats_on_between? start, stop, interval
27
19
  end
28
20
 
29
21
  def schedule
@@ -31,12 +23,14 @@ module NetworkExecutive
31
23
  end
32
24
 
33
25
  module ClassMethods
34
- def schedule
26
+ def schedule( program = nil, options = {}, &block )
35
27
  @schedule ||= ChannelSchedule.new
36
- end
37
28
 
38
- def every( date, options )
39
- schedule.add ProgramSchedule.new( date, options )
29
+ if block_given?
30
+ @schedule.add program, options, &block
31
+ else
32
+ @schedule
33
+ end
40
34
  end
41
35
  end
42
36