network_executive 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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