poms 0.0.3

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: 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