poms 0.0.3

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2e3aa1829a203b60720b24083855ffb8a9f41621
4
+ data.tar.gz: 60ddc33138bde526bf72892201877abce90dbc29
5
+ SHA512:
6
+ metadata.gz: 755e683ca19d2c23d29555426309d3618bfe2b0dcb30651341053c9332b8c61db00e0968dea06a1105ec9bf55b1c85ba71a867eeef1e7c7b29e3edf70480c3cb
7
+ data.tar.gz: 8f4e26864b024263e36f830cad01cd56a915c438d979c0ef3304b2d0217661f41d21f0fddb606029eec76ebf70e9b7aadfebb2549d699154d46a93393759bad7
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in poms.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,80 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ poms (0.0.3)
5
+ activesupport
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (4.1.5)
11
+ i18n (~> 0.6, >= 0.6.9)
12
+ json (~> 1.7, >= 1.7.7)
13
+ minitest (~> 5.1)
14
+ thread_safe (~> 0.1)
15
+ tzinfo (~> 1.1)
16
+ celluloid (0.15.2)
17
+ timers (~> 1.1.0)
18
+ coderay (1.1.0)
19
+ diff-lcs (1.2.5)
20
+ fabrication (2.11.3)
21
+ fakeweb (1.3.0)
22
+ ffi (1.9.3)
23
+ formatador (0.2.5)
24
+ guard (2.6.1)
25
+ formatador (>= 0.2.4)
26
+ listen (~> 2.7)
27
+ lumberjack (~> 1.0)
28
+ pry (>= 0.9.12)
29
+ thor (>= 0.18.1)
30
+ guard-rspec (4.3.1)
31
+ guard (~> 2.1)
32
+ rspec (>= 2.14, < 4.0)
33
+ i18n (0.6.11)
34
+ json (1.8.1)
35
+ listen (2.7.9)
36
+ celluloid (>= 0.15.2)
37
+ rb-fsevent (>= 0.9.3)
38
+ rb-inotify (>= 0.9)
39
+ lumberjack (1.0.9)
40
+ method_source (0.8.2)
41
+ minitest (5.4.1)
42
+ pry (0.10.1)
43
+ coderay (~> 1.1.0)
44
+ method_source (~> 0.8.1)
45
+ slop (~> 3.4)
46
+ rake (10.3.2)
47
+ rb-fsevent (0.9.4)
48
+ rb-inotify (0.9.5)
49
+ ffi (>= 0.5.0)
50
+ rspec (3.0.0)
51
+ rspec-core (~> 3.0.0)
52
+ rspec-expectations (~> 3.0.0)
53
+ rspec-mocks (~> 3.0.0)
54
+ rspec-core (3.0.4)
55
+ rspec-support (~> 3.0.0)
56
+ rspec-expectations (3.0.4)
57
+ diff-lcs (>= 1.2.0, < 2.0)
58
+ rspec-support (~> 3.0.0)
59
+ rspec-mocks (3.0.4)
60
+ rspec-support (~> 3.0.0)
61
+ rspec-support (3.0.4)
62
+ slop (3.6.0)
63
+ thor (0.19.1)
64
+ thread_safe (0.3.4)
65
+ timers (1.1.0)
66
+ tzinfo (1.2.2)
67
+ thread_safe (~> 0.1)
68
+
69
+ PLATFORMS
70
+ ruby
71
+
72
+ DEPENDENCIES
73
+ bundler (~> 1.3)
74
+ fabrication
75
+ fakeweb
76
+ guard
77
+ guard-rspec
78
+ poms!
79
+ rake
80
+ rspec
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
10
+ watch('config/routes.rb') { "spec/routing" }
11
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Brightin BV
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # Poms
2
+
3
+ The Poms gem provides an interface to the Dutch public broadcaster API: POMS. It
4
+ is a couchdb service that is publicly available.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'poms'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install poms
19
+
20
+ ## Contributing
21
+
22
+ 1. Fork it
23
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
24
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
25
+ 4. Push to the branch (`git push origin my-new-feature`)
26
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/lib/poms.rb ADDED
@@ -0,0 +1,110 @@
1
+ require 'active_support'
2
+ require 'open-uri'
3
+ require 'json'
4
+ require 'poms/base'
5
+ require 'poms/version'
6
+ require 'poms/builder'
7
+ require 'poms/schedule_event'
8
+ require 'poms/broadcast'
9
+ require 'poms/season'
10
+ require 'poms/series'
11
+
12
+ module Poms
13
+ extend self
14
+ URL = 'http://docs.poms.omroep.nl'
15
+ MEDIA_PATH = '/media/'
16
+ BROADCASTS_VIEW_PATH = '/media/_design/media/_view/broadcasts-by-broadcaster-and-start'
17
+ ANCESTOR_AND_TYPE_PATH = '/media/_design/media/_view/by-ancestor-and-type'
18
+ ANCESTOR_AND_SORTDATE_PATH = '/media/_design/media/_view/by-ancestor-and-sortdate'
19
+ CHANNEL_AND_START_PATH = '/media/_design/media/_view/broadcasts-by-channel-and-start'
20
+ VALID_CHANNELS = /^NED(1|2|3)$/
21
+ # ?startkey=[\"Zapp\",1369755130000]&endkey=[\"Zapp\",1370964770000]&reduce=false&include_docs=true
22
+
23
+ def fetch(mid)
24
+ return nil if mid.nil?
25
+ hash = fetch_raw_json mid
26
+ Poms::Builder.process_hash hash
27
+ end
28
+
29
+ def fetch_raw_json(mid)
30
+ uri = [MEDIA_PATH, mid].join
31
+ get_json(uri)
32
+ end
33
+
34
+ def upcoming_broadcasts(zender, start_time = Time.now, end_time = Time.now+7.days)
35
+ uri = [BROADCASTS_VIEW_PATH, broadcast_view_params(zender, start_time, end_time )].join
36
+ hash = get_json(uri)
37
+ hash['rows'].map {|item| Poms::Builder.process_hash item['doc']}
38
+ end
39
+
40
+ def fetch_descendants_for_serie(mid, type='BROADCAST')
41
+ uri = [ANCESTOR_AND_TYPE_PATH, ancestor_type_params(mid, type) ].join
42
+ hash = get_json(uri) || {'rows' => []}
43
+ hash['rows'].map {|item| Poms::Builder.process_hash item['doc']}
44
+ end
45
+
46
+ alias_method :fetch_broadcasts_for_serie, :fetch_descendants_for_serie
47
+
48
+ def fetch_descendants_by_date_for_serie(mid, start_time=1.week.ago)
49
+ uri = [ANCESTOR_AND_SORTDATE_PATH, ancestor_sortdate_params(mid, start_time) ].join
50
+ hash = get_json(uri) || {'rows' => []}
51
+ hash['rows'].map {|item| Poms::Builder.process_hash item['doc']}
52
+ end
53
+
54
+ def fetch_descendant_mids(mid, type='BROADCAST')
55
+ uri = [ANCESTOR_AND_TYPE_PATH, ancestor_type_params(mid, type), "&include_docs=false"].join
56
+ hash = get_json(uri) || {'rows' => []}
57
+ hash['rows'].map {|item| item['id']}
58
+ end
59
+
60
+ def fetch_broadcasts_by_channel_and_start(channel, start_time=1.week.ago, end_time=Time.now)
61
+ uri = [CHANNEL_AND_START_PATH, channel_params(channel, start_time, end_time) ].join
62
+ hash = get_json(uri)
63
+ hash['rows'].map {|item| Poms::Builder.process_hash item['doc']}
64
+ end
65
+
66
+ def fetch_current_broadcast(channel)
67
+ uri = [CHANNEL_AND_START_PATH, channel_params(channel, Time.now, 1.day.ago), '&descending=true&limit=1' ].join
68
+ hash = get_json(uri)
69
+ rows = hash['rows']
70
+ Poms::Builder.process_hash(rows.empty? ? {} : rows.first['doc'])
71
+ end
72
+
73
+ def fetch_next_broadcast(channel)
74
+ uri = [CHANNEL_AND_START_PATH, channel_params(channel, Time.now, 1.day.from_now), '&limit=1' ].join
75
+ hash = get_json(uri)
76
+ rows = hash['rows']
77
+ Poms::Builder.process_hash(rows.empty? ? {} : rows.first['doc'])
78
+ end
79
+
80
+ # private
81
+ def broadcast_view_params(zender, start_time, end_time)
82
+ zender = zender.capitalize
83
+ "?startkey=[\"#{zender}\",#{start_time.to_i * 1000}]&endkey=[\"#{zender}\",#{end_time.to_i * 1000}]&reduce=false&include_docs=true"
84
+ end
85
+
86
+ def ancestor_type_params(mid, type)
87
+ "?reduce=false&key=[\"#{mid}\",\"#{type}\"]&include_docs=true"
88
+ end
89
+
90
+ def ancestor_sortdate_params(mid, start_time=1.month.ago)
91
+ end_time_i = 1.week.from_now.to_i * 1000
92
+ start_time_i = start_time.to_i * 1000
93
+ "?reduce=false&startkey=[\"#{mid}\",#{start_time_i}]&endkey=[\"#{mid}\",#{end_time_i}]&include_docs=true"
94
+ end
95
+
96
+
97
+ def channel_params(channel, start_time, end_time)
98
+ "?startkey=[\"#{channel}\",#{start_time.to_i * 1000}]&endkey=[\"#{channel}\",#{end_time.to_i * 1000}]&reduce=false&include_docs=true"
99
+ end
100
+
101
+ def get_json(uri)
102
+ begin
103
+ JSON.parse(open(URI.escape [URL, uri].join).read)
104
+ rescue OpenURI::HTTPError => e
105
+ raise e unless e.message.match(/404/)
106
+ nil
107
+ end
108
+ end
109
+
110
+ end
data/lib/poms/base.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Poms
2
+ module Base
3
+ def rev
4
+ _rev.to_i
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,46 @@
1
+ require 'poms/has_ancestors'
2
+ require 'poms/has_base_attributes'
3
+
4
+ module Poms
5
+ class Broadcast < Poms::Builder::NestedOpenStruct
6
+
7
+ include Poms::HasAncestors
8
+ include Poms::HasBaseAttributes
9
+
10
+ def initialize hash
11
+ super
12
+ process_schedule_events
13
+ end
14
+
15
+ def process_schedule_events
16
+ if schedule_events
17
+ schedule_events.select! { |e| e.channel.match Poms::VALID_CHANNELS }
18
+ end
19
+ self.schedule_events = schedule_events.map { |e| Poms::ScheduleEvent.new e.marshal_dump } if schedule_events
20
+ end
21
+
22
+ def series_mid
23
+ serie.try :mid_ref || serie.mid
24
+ end
25
+
26
+ def odi_streams
27
+ return [] if locations.nil? or locations.empty?
28
+ odi_streams = locations.select { |l| l.program_url.match(/^odi/) }
29
+ streams = odi_streams.map do |l|
30
+ l.program_url.match(/^[\w+]+\:\/\/[\w\.]+\/video\/(\w+)\/\w+/)[1]
31
+ end
32
+ streams.uniq
33
+ end
34
+
35
+ def available_until
36
+ return nil if locations.nil? or locations.empty?
37
+ timestamp = locations.map(&:publish_stop).compact.first
38
+ return Time.at(timestamp / 1000).to_datetime if timestamp
39
+ end
40
+
41
+ end
42
+
43
+ class Strand < Broadcast
44
+ end
45
+
46
+ end
@@ -0,0 +1,78 @@
1
+ require 'ostruct'
2
+ require 'active_support/all'
3
+
4
+ module Poms
5
+ class Builder
6
+ def self.process_hash(hash)
7
+ return unless hash
8
+ underscored_hash = {}
9
+ hash.each { |k,v| underscored_hash[k.underscore] = v }
10
+ class_name = (underscored_hash['type'] || "Typeless").capitalize
11
+ begin
12
+ klass = Poms.const_get class_name
13
+ rescue NameError
14
+ # c = Class.new(Poms::NestedOpenStruct)
15
+ klass = Poms.const_set class_name, Class.new(Poms::Builder::NestedOpenStruct)
16
+ end
17
+ klass.send(:new, underscored_hash)
18
+ end
19
+
20
+ private
21
+
22
+
23
+ class NestedOpenStruct < OpenStruct
24
+
25
+ include Poms::Base
26
+
27
+ def initialize(hash)
28
+ @hash = hash
29
+ @hash.each do |k,v|
30
+ process_key_value(k,v)
31
+ end
32
+ super hash
33
+ end
34
+
35
+ private
36
+
37
+ def process_key_value(k, v)
38
+ case v
39
+ when Array
40
+ struct_array = v.map do |element|
41
+ process_element(element)
42
+ end
43
+ @hash.send("[]=", k, struct_array)
44
+ when Hash
45
+ @hash.send("[]=", k, Poms::Builder.process_hash(v))
46
+ when String, Integer
47
+
48
+ case k
49
+ when "start", "end", "sort_date"
50
+ @hash.send("[]=", k, Time.at(v / 1000))
51
+ end
52
+ when NilClass, FalseClass, TrueClass, Time, Poms::Typeless
53
+ # do nothing
54
+ else
55
+ raise Poms::Exceptions::UnkownStructure, "Error processing #{v.class}: #{v}, which was expected to be a String or Array"
56
+ end
57
+ end
58
+
59
+ def process_element(element)
60
+ case element
61
+ when String, Integer
62
+ element
63
+ when Hash
64
+ Poms::Builder.process_hash element
65
+ else
66
+ raise Poms::Exceptions::UnkownStructure, "Error processing #{element}: which was expected to be a String nor a Hash"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ module Exceptions
74
+ class UnkownStructure < StandardError
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,42 @@
1
+ module Poms
2
+ module HasAncestors
3
+ module ClassMethods
4
+
5
+ end
6
+
7
+ module InstanceMethods
8
+ def series
9
+ return @series if @series
10
+ descendant_series = descendant_of.reject { |obj| obj.class != Poms::Series }
11
+ if descendant_of.blank?
12
+ []
13
+ elsif descendant_series.blank?
14
+ descendant_of
15
+ else
16
+ descendant_series
17
+ end
18
+ end
19
+
20
+ def serie
21
+ series.first
22
+ end
23
+
24
+ def serie_mid
25
+ return nil if serie.nil?
26
+ serie.mid_ref || serie.mid
27
+ end
28
+
29
+ def ancestor_mids
30
+ return @ancestor_mids if @ancestor_mids
31
+ descendant_of_mids = descendant_of.map(&:mid_ref) rescue []
32
+ episode_of_mids = episode_of.map(&:mid_ref) rescue []
33
+ @ancestor_mids = (descendant_of_mids + episode_of_mids).flatten.compact.uniq
34
+ end
35
+ end
36
+
37
+ def self.included(receiver)
38
+ receiver.extend ClassMethods
39
+ receiver.send :include, InstanceMethods
40
+ end
41
+ end
42
+ end