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.
- data/README.md +53 -24
- data/app/assets/fonts/network_executive/league_gothic.css +13 -0
- data/app/assets/fonts/network_executive/league_gothic.svg +148 -0
- data/app/assets/fonts/network_executive/league_gothic.ttf +0 -0
- data/app/assets/fonts/network_executive/league_gothic.woff +0 -0
- data/app/assets/javascripts/network_executive/components/iframe_notifier.coffee +14 -0
- data/app/assets/javascripts/network_executive/components/namespace.coffee +1 -0
- data/app/assets/javascripts/network_executive/components/osd.coffee +65 -0
- data/app/assets/javascripts/network_executive/components/photo_player.coffee +55 -0
- data/app/assets/javascripts/network_executive/components/postman.coffee +23 -0
- data/app/assets/javascripts/network_executive/components/set_top_box.coffee +81 -0
- data/app/assets/javascripts/network_executive/components/tweet_player.coffee +95 -0
- data/app/assets/javascripts/network_executive/components/you_tube_player.coffee +85 -0
- data/app/assets/javascripts/network_executive/slideshow.coffee +6 -0
- data/app/assets/javascripts/network_executive/twitter.coffee +7 -0
- data/app/assets/javascripts/network_executive/you_tube.coffee +8 -0
- data/app/assets/javascripts/network_executive.coffee +10 -0
- data/app/assets/stylesheets/network_executive/{gui_components.css → components/gui_components.css} +0 -0
- data/app/assets/stylesheets/network_executive/{normalize.css → components/normalize.css} +0 -0
- data/app/assets/stylesheets/network_executive/{osd.css → components/osd.css} +6 -34
- data/app/assets/stylesheets/network_executive/components/osd_guide.css +43 -0
- data/app/assets/stylesheets/network_executive/components/photo_player.css +61 -0
- data/app/assets/stylesheets/network_executive/{smpte.css → components/smpte.css} +0 -0
- data/app/assets/stylesheets/network_executive/components/tweet_player.css +60 -0
- data/app/assets/stylesheets/network_executive/components/you_tube_player.css +12 -0
- data/app/assets/stylesheets/network_executive/off_air.css +5 -0
- data/app/assets/stylesheets/network_executive/slideshow.css +4 -0
- data/app/assets/stylesheets/network_executive/twitter.css +5 -0
- data/app/assets/stylesheets/network_executive/you_tube.css +3 -0
- data/app/assets/stylesheets/{network_executive/application.css → network_executive.css} +5 -5
- data/app/controllers/network_executive/components_controller.rb +4 -0
- data/app/controllers/network_executive/guide_controller.rb +41 -0
- data/app/controllers/network_executive/programs_controller.rb +9 -0
- data/app/helpers/network_executive/network_helper.rb +6 -0
- data/app/models/network_executive/channel.rb +15 -14
- data/app/models/network_executive/guide.rb +65 -0
- data/app/models/network_executive/program.rb +69 -5
- data/app/models/network_executive/viewer.rb +4 -0
- data/app/programs/network_executive/off_air.rb +18 -0
- data/app/views/network_executive/components/slideshow.html.erb +20 -0
- data/app/views/network_executive/components/twitter.html.erb +16 -0
- data/app/views/network_executive/components/you_tube.html.erb +13 -0
- data/app/views/network_executive/guide/index.html.erb +30 -0
- data/app/views/network_executive/network/index.html.erb +3 -3
- data/app/views/network_executive/programs/off_air.html.erb +13 -0
- data/config/routes.rb +7 -1
- data/lib/network_executive/channel_schedule.rb +65 -0
- data/lib/network_executive/components/photo_player.rb +48 -0
- data/lib/network_executive/components/tweet_player.rb +48 -0
- data/lib/network_executive/components/you_tube_player.rb +15 -0
- data/lib/network_executive/components.rb +3 -0
- data/lib/network_executive/engine.rb +29 -3
- data/lib/network_executive/off_air_schedule.rb +18 -0
- data/lib/network_executive/producer.rb +3 -3
- data/lib/network_executive/program_schedule.rb +96 -0
- data/lib/network_executive/program_schedule_proxy.rb +55 -0
- data/lib/network_executive/scheduled_program.rb +26 -0
- data/lib/network_executive/scheduling.rb +15 -21
- data/lib/network_executive/time_calculations.rb +10 -0
- data/lib/network_executive/version.rb +1 -1
- data/lib/network_executive.rb +2 -1
- data/network_executive.gemspec +5 -0
- data/spec/models/channel_spec.rb +30 -12
- data/spec/models/{lineup_spec.rb → guide_spec.rb} +28 -6
- data/spec/models/program_spec.rb +46 -3
- data/spec/models/viewer_spec.rb +9 -1
- data/spec/programs/off_air_spec.rb +9 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/unit/channel_schedule_spec.rb +67 -0
- data/spec/unit/components/photo_player_spec.rb +76 -0
- data/spec/unit/components/tweet_player_spec.rb +68 -0
- data/spec/unit/components/you_tube_player_spec.rb +17 -0
- data/spec/unit/off_air_schedule_spec.rb +28 -0
- data/spec/unit/producer_spec.rb +2 -4
- data/spec/unit/program_schedule_proxy_spec.rb +102 -0
- data/spec/unit/program_schedule_spec.rb +191 -0
- data/spec/unit/scheduled_program_spec.rb +57 -0
- data/spec/unit/scheduling_spec.rb +61 -72
- data/vendor/assets/javascripts/bigtext.js +264 -0
- metadata +185 -44
- data/app/assets/javascripts/application.js +0 -13
- data/app/assets/javascripts/network_executive/osd.js +0 -62
- data/app/assets/javascripts/network_executive/set_top_box.js +0 -45
- data/app/controllers/network_executive/lineup_controller.rb +0 -11
- data/app/models/network_executive/channel_schedule.rb +0 -13
- data/app/models/network_executive/lineup.rb +0 -74
- data/app/models/network_executive/lineup_range.rb +0 -34
- data/app/models/network_executive/program_schedule.rb +0 -148
- data/app/views/network_executive/lineup/index.html.erb +0 -26
- data/spec/models/channel_schedule_spec.rb +0 -29
- data/spec/models/lineup_range_spec.rb +0 -65
- data/spec/models/program_schedule_spec.rb +0 -399
|
@@ -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 %>%;"> </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
|
|
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="
|
|
31
|
+
<article id="guide" class="guide"></article>
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
|
-
<%= javascript_include_tag '
|
|
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 '
|
|
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
|
|
@@ -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
|
|
16
|
-
|
|
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, '
|
|
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
|
-
|
|
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
|
|
8
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
def whats_on?
|
|
14
|
+
whats_on_at? Time.now.change(sec:0)
|
|
15
|
+
end
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
39
|
-
|
|
29
|
+
if block_given?
|
|
30
|
+
@schedule.add program, options, &block
|
|
31
|
+
else
|
|
32
|
+
@schedule
|
|
33
|
+
end
|
|
40
34
|
end
|
|
41
35
|
end
|
|
42
36
|
|