helix 0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012, Twistage
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of Twistage nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL Twistage BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,169 @@
1
+ http://nestacms.com/docs/creating-content/markdown-cheat-sheet
2
+ http://support.mashery.com/docs/customizing_your_portal/Markdown_Cheat_Sheet
3
+
4
+ # Helix
5
+
6
+ The Helix gem allows developers to easily connect to and manipulate the Twistage API.
7
+
8
+ Documentation
9
+ -------------
10
+
11
+ You should find the documentation for your version of helix on [Rubygems](https://rubygems.org/gems/helix).
12
+
13
+ Install
14
+ --------
15
+
16
+ ```shell
17
+ gem install helix
18
+ ```
19
+ or add the following line to Gemfile:
20
+
21
+ ```ruby
22
+ gem 'helix'
23
+ ```
24
+ and run `bundle install` from your shell.
25
+
26
+ Install From Repo
27
+ -----------------
28
+ Using sudo:
29
+ ```shell
30
+ git clone git@github.com:Twistage/helix.git
31
+ gem build helix.gemspec
32
+ sudo gem i helix-*.gem
33
+ ```
34
+
35
+ RVM or root support:
36
+ ```shell
37
+ git clone git@github.com:Twistage/helix.git
38
+ gem build helix.gemspec
39
+ gem i helix-*.gem
40
+ ```
41
+
42
+ Rebuilding the gem, use the first for sudo, the second for RVM or root:
43
+ ```shell
44
+ rake reinstall_helix
45
+ rake reinstall_helix_rvm
46
+ ```
47
+
48
+ Using gem in a Gemfile
49
+ ```shell
50
+ gem 'helix', :git => git@github.com:Twistage/helix.git
51
+ ```
52
+
53
+
54
+
55
+ Supported Ruby versions
56
+ -----------------------
57
+
58
+ 1.9.3
59
+ 1.9.2
60
+
61
+ How To
62
+ ------
63
+
64
+ ###Setup YAML
65
+ ```yaml
66
+ site: 'http://service.twistage.com'
67
+ user: 'my_account@twistage.com'
68
+ password: 'password123'
69
+ company: 'my_company'
70
+ license_key: '141a86b5c4091
71
+ ```
72
+ Load the YAML file as your config.
73
+ ```ruby
74
+ Helix::Config.load("path/to/yaml.yml")
75
+ ```
76
+ ####Current CRUD methods supported by all models
77
+ .create
78
+ .find
79
+ \#update
80
+ \#destroy
81
+
82
+ ####Current models
83
+ Videos, Images, Albums, Tracks, Playlists
84
+
85
+ ###Videos
86
+ #####Required fields: title, description, library, company, and source.
87
+ ```ruby
88
+ video = Helix::Video.create!( title: 'New Video',
89
+ description: 'A video of new things',
90
+ source: 'http://somesource.com/source.mp4'
91
+ company: 'some_company',
92
+ library: 'some_library')
93
+ video.update({title: "New Title"})
94
+ another_video = Helix::Video.find(some_guid)
95
+ another_video.destroy
96
+ ```
97
+ ###Albums
98
+ #####Required fields: title, library, company.
99
+ ```ruby
100
+ album = Helix::Album.create!( title: 'New Album',
101
+ description: 'A album of new things',
102
+ source: 'http://somesource.com/source.mp4'
103
+ company: 'some_company',
104
+ library: 'some_library')
105
+ #Update for album is not currently supported
106
+ another_album = Helix::Album.find(some_guid)
107
+ another_album.destroy
108
+ ```
109
+ ###Images
110
+ #####Required fields: title, description, library, company, and source.
111
+ ```ruby
112
+ image = Helix::Image.create!( title: 'New Image',
113
+ description: 'A image of new things',
114
+ source: 'http://somesource.com/source.jpg'
115
+ company: 'some_company',
116
+ library: 'some_library')
117
+ image.update({title: "New Title"})
118
+ another_image = Helix::Image.find(some_guid)
119
+ another_image.destroy
120
+ ```
121
+ ###Tracks
122
+ #####Required fields: title, description, library, company, and source.
123
+ ```ruby
124
+ track = Helix::Track.create!( title: 'New Track',
125
+ description: 'A track of new things',
126
+ source: 'http://somesource.com/source.mp3'
127
+ company: 'some_company',
128
+ library: 'some_library')
129
+ track.update({title: "New Title"})
130
+ another_track = Helix::Track.find(some_guid)
131
+ another_track.destroy
132
+ ```
133
+ ###Playlists
134
+ #####Required fields: title, library, company.
135
+ ```ruby
136
+ playlist = Helix::Playlist.create!( title: 'New Playlist',
137
+ description: 'A playlist of new things',
138
+ source: 'http://somesource.com/source.mp4'
139
+ company: 'some_company',
140
+ library: 'some_library')
141
+ playlist.update({title: "New Title"})
142
+ another_playlist = Helix::Playlist.find(some_guid)
143
+ another_playlist.destroy
144
+ ```
145
+
146
+ More Information
147
+ ----------------
148
+
149
+ * [Rubygems](https://rubygems.org/gems/helix)
150
+ * [Issues](https://github.com/twistage/helix/issues)
151
+
152
+ Contributing
153
+ ------------
154
+
155
+ How to contribute
156
+
157
+ Credits
158
+ -------
159
+
160
+ Helix was written by Kevin Baird and Michael Wood.
161
+
162
+ Helix is maintained and funded by [Twistage, inc](http://twistage.com)
163
+
164
+ The names and logos for twistage are trademarks of twistage, inc.
165
+
166
+ License
167
+ -------
168
+
169
+ Helix is Copyright © 2008-2012 Twistage, inc.
@@ -0,0 +1,27 @@
1
+ require 'helix/base'
2
+
3
+ module Helix
4
+
5
+ class Album < Base
6
+
7
+ # The class name, to be used by supporting classes. Such as Config which uses
8
+ # this method as a way to build URLs.
9
+ #
10
+ #
11
+ # @example
12
+ # Helix::Album.media_type_sym #=> :album
13
+ #
14
+ # @return [Symbol] Name of the class.
15
+ def self.media_type_sym; :album; end
16
+
17
+ # Currently update is unsupported for album.
18
+ #
19
+ # @param [Hash] opts an array can be passed in so it remains functionally similiar to other update calls.
20
+ # @return [Exception] "Albums Update is not currently supported."
21
+ def update(opts={})
22
+ raise "Albums Update is not currently supported."
23
+ end
24
+
25
+ end
26
+
27
+ end
data/lib/helix/base.rb ADDED
@@ -0,0 +1,185 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+ require 'yaml'
4
+ require 'crack'
5
+
6
+ module Helix
7
+ class Base
8
+
9
+ unless defined?(self::METHODS_DELEGATED_TO_CLASS)
10
+ METHODS_DELEGATED_TO_CLASS = [ :guid_name, :media_type_sym, :plural_media_type ]
11
+ end
12
+
13
+ attr_accessor :attributes, :config
14
+
15
+ # Creates a new record via API and then returns an instance of that record.
16
+ #
17
+ # Example is using Video class since Video inherits from Base. This won't
18
+ # normally be called as Helix::Base.create
19
+ #
20
+ # @example
21
+ # Helix::Video.create({title: "My new video"})
22
+ #
23
+ # @param [Hash] attributes a hash containing the attributes used in the create
24
+ # @return [Base] An instance of Helix::Base
25
+ def self.create(attributes={})
26
+ config = Helix::Config.instance
27
+ #xml = '<?xml version="1.0" encoding="UTF-8"?>
28
+ # <add>
29
+ # <list>
30
+ # <entry>
31
+ # <src>http://www.minhreigen.com/videos/play.mp4</src>
32
+ # <title>Summer camp dance</title>
33
+ # <description>This one is the best</description>
34
+ # </entry>
35
+ # </list>
36
+ # </add>'.gsub!(/\s{2,}|\n/, "")
37
+ url = config.build_url(media_type: plural_media_type,
38
+ format: :xml)
39
+ response = RestClient.post(url, attributes.merge(signature: config.signature(:update)))
40
+ attrs = Crack::XML.parse(response)
41
+ self.new(attributes: attrs[media_type_sym.to_s], config: config)
42
+ end
43
+
44
+ # Finds and returns a record in instance form for a class, through
45
+ # guid lookup.
46
+ #
47
+ # @example
48
+ # video_guid = "8e0701c142ab1"
49
+ # video = Helix::Video.find(video_guid)
50
+ #
51
+ # @param [String] guid an id in guid form.
52
+ # @return [Base] An instance of Helix::Base
53
+ def self.find(guid)
54
+ config = Helix::Config.instance
55
+ item = self.new(attributes: { guid_name => guid }, config: config)
56
+ item.load
57
+ end
58
+
59
+ # Fetches all accessible records, places them into instances, and returns
60
+ # them as an array.
61
+ #
62
+ # @example
63
+ # Helix::Video.find_all(query: 'string_to_match') #=> [video1,video2]
64
+ #
65
+ # @param [Hash] opts a hash of options for parameters passed into the HTTP GET
66
+ # @return [Array] The array of instance objects for a class.
67
+ def self.find_all(opts)
68
+ data_sets = get_data_sets(opts)
69
+ return [] if data_sets.nil?
70
+ data_sets.map { |attrs| self.new(attributes: attrs) }
71
+ end
72
+
73
+ def self.get_data_sets(opts)
74
+ config = Helix::Config.instance
75
+ url = config.build_url(format: :json)
76
+ # We allow opts[:sig_type] for internal negative testing only.
77
+ raw_response = config.get_response(url, {sig_type: :view}.merge(opts))
78
+ data_sets = raw_response[plural_media_type]
79
+ end
80
+
81
+ # Creates a string that associates to the class id.
82
+ #
83
+ # @example
84
+ # Helix::Video.guid_name #=> "video_id"
85
+ #
86
+ # @return [String] The guid name for a specific class.
87
+ def self.guid_name
88
+ "#{self.media_type_sym}_id"
89
+ end
90
+
91
+ # Creates a string associated with a class name pluralized
92
+ #
93
+ # @example
94
+ # Helix::Video.plural_media_type #=> "videos"
95
+ #
96
+ # @return [String] The class name pluralized
97
+ def self.plural_media_type
98
+ "#{self.media_type_sym}s"
99
+ end
100
+
101
+ METHODS_DELEGATED_TO_CLASS.each do |meth|
102
+ define_method(meth) { |*args| self.class.send(meth, *args) }
103
+ end
104
+
105
+ def initialize(opts)
106
+ @attributes = opts[:attributes]
107
+ @config = opts[:config]
108
+ end
109
+
110
+ # Deletes the record of the Helix::Base instance.
111
+ #
112
+ # @example
113
+ # video = Helix::Video.create({title: "Some Title"})
114
+ # video.destroy
115
+ #
116
+ # @return [String] The response from the HTTP DELETE call.
117
+ def destroy
118
+ memo_cfg = config
119
+ url = memo_cfg.build_url(format: :xml, guid: guid, media_type: plural_media_type)
120
+ RestClient.delete(url, params: {signature: memo_cfg.signature(:update)})
121
+ end
122
+
123
+ # Creates a string that associates to the class id.
124
+ #
125
+ # @example
126
+ # video = Helix::Video.create({title: "My new title"})
127
+ # video.guid #=> "9e0989v234sf4"
128
+ #
129
+ # @return [String] The guid for the class instance.
130
+ def guid
131
+ @attributes[guid_name]
132
+ end
133
+
134
+ # Loads in the record from a HTTP GET response.
135
+ #
136
+ # @param [Hash] opts a hash of attributes to update the instance with.
137
+ # @return [Base] Returns an instance of the class.
138
+ def load(opts={})
139
+ memo_cfg = config
140
+ url = memo_cfg.build_url(format: :json, guid: self.guid, media_type: plural_media_type)
141
+ # We allow opts[:sig_type] for internal negative testing only.
142
+ raw_attrs = memo_cfg.get_response(url, {sig_type: :view}.merge(opts))
143
+ @attributes = massage_raw_attrs(raw_attrs)
144
+ self
145
+ end
146
+ alias_method :reload, :load
147
+
148
+ # Raises an error for missing method calls.
149
+ #
150
+ # @param [Symbol] method_sym The method attempting to be called.
151
+ # @return [String] An error for the method attempting to be called.
152
+ def method_missing(method_sym)
153
+ begin
154
+ @attributes[method_sym.to_s]
155
+ rescue
156
+ raise NoMethodError, "#{method_sym} is not recognized within #{self.class.to_s}'s @attributes"
157
+ end
158
+ end
159
+
160
+ # Updates instance and record with attributes passed in.
161
+ #
162
+ # @example
163
+ # video = Helix::Video.find(video_guid)
164
+ # video.update({title: "My new title"})
165
+ #
166
+ # @param [Hash] opts a hash of attributes to update the instance with.
167
+ # @return [Base] Returns an instance of the class after update.
168
+ def update(opts={})
169
+ memo_cfg = config
170
+ url = memo_cfg.build_url(format: :xml, guid: guid, media_type: plural_media_type)
171
+ params = {signature: memo_cfg.signature(:update)}.merge(media_type_sym => opts)
172
+ RestClient.put(url, params)
173
+ self
174
+ end
175
+
176
+ private
177
+
178
+ def massage_raw_attrs(raw_attrs)
179
+ # FIXME: Albums JSON output is embedded as the only member of an Array.
180
+ proper_hash = raw_attrs.respond_to?(:has_key?) && raw_attrs.has_key?(guid_name)
181
+ proper_hash ? raw_attrs : raw_attrs.first
182
+ end
183
+
184
+ end
185
+ end
@@ -0,0 +1,150 @@
1
+ require 'helix/video'
2
+ require 'helix/track'
3
+ require 'helix/album'
4
+ require 'helix/image'
5
+ require 'singleton'
6
+
7
+ module Helix
8
+
9
+ class Config
10
+
11
+ include Singleton
12
+
13
+ unless defined?(self::DEFAULT_FILENAME)
14
+ DEFAULT_FILENAME = './helix.yml'
15
+ SCOPES = %w(reseller company library)
16
+ SIG_DURATION = 1200 # in minutes
17
+ TIME_OFFSET = 1000 * 60 # 1000 minutes, lower to give some margin of error
18
+ VALID_SIG_TYPES = [ :ingest, :update, :view ]
19
+ end
20
+
21
+ attr_accessor :credentials
22
+
23
+ # Creates a singleton of itself, setting the config
24
+ # to a specified YAML file. If no file is specified the default
25
+ # helix.yml file is used.
26
+ #
27
+ # @example
28
+ # Helix::Config.load("/some/path/my_yaml.yml")
29
+ # video = Helix::Video.find("8e0701c142ab1") #Uses my_yaml.yml
30
+ #
31
+ # @param [String] yaml_file_location the yaml file used for config
32
+ # @return [Helix::Config] config returns singleton of Helix::Config
33
+ def self.load(yaml_file_location = DEFAULT_FILENAME)
34
+ config = self.instance
35
+ config.instance_variable_set(:@filename, yaml_file_location)
36
+ config.instance_variable_set(:@credentials, YAML.load(File.open(yaml_file_location)))
37
+ config
38
+ end
39
+
40
+ # Creates additional URL stubbing that can be used in conjuction
41
+ # with the base_url to create RESTful URLs
42
+ #
43
+ # @param [String] base_url the base part of the URL to be used
44
+ # @param [Hash] opts a hash of options for building URL additions
45
+ # @return [String] The full RESTful URL string object
46
+ def add_sub_urls(base_url, opts)
47
+ guid, action = [:guid, :action].map { |sub| opts[sub] }
48
+ url = "#{base_url}/#{opts[:media_type]}"
49
+ url += "/#{guid}" if guid
50
+ url += "/#{action}" if action
51
+ "#{url}.#{opts[:format]}"
52
+ end
53
+
54
+ # Creates a full RESTful URL to be used for HTTP requests.
55
+ #
56
+ # @param [Hash] opts a hash of options for building URL
57
+ # @return [String] The full RESTful URL string object
58
+ def build_url(opts={})
59
+ opts[:format] ||= :json
60
+ opts[:media_type] ||= :videos
61
+ base_url = get_base_url(opts)
62
+ url = add_sub_urls(base_url, opts)
63
+ end
64
+
65
+ # Creates the base url with information collected from credentials.
66
+ #
67
+ # @param [Hash] opts a hash of options for building URL
68
+ # @return [String] The base RESTful URL string object
69
+ def get_base_url(opts)
70
+ creds = credentials
71
+ base_url = creds['site']
72
+ reseller, company, library = SCOPES.map { |scope| creds[scope] }
73
+ base_url += "/resellers/#{reseller}" if reseller
74
+ if company
75
+ base_url += "/companies/#{company}"
76
+ base_url += "/libraries/#{library}" if library
77
+ end
78
+ base_url
79
+ end
80
+
81
+ # Creates additional URL stubbing that can be used in conjuction
82
+ # with the base_url to create RESTful URLs
83
+ #
84
+ # @param [String] url the base part of the URL to be used
85
+ # @param [Hash] opts a hash of options for building URL additions
86
+ # @return [String] The full RESTful URL string object
87
+ def get_response(url, opts={})
88
+ sig_type = opts.delete(:sig_type)
89
+ params = opts.merge(signature: signature(sig_type, opts))
90
+ response = RestClient.get(url, params: params)
91
+ JSON.parse(response)
92
+ end
93
+
94
+ # Fetches the signature for a specific license key.
95
+ #
96
+ # @param [Symbol] sig_type The type of signature required for calls.
97
+ # @return [String] The signature needed to pass around for calls.
98
+ def signature(sig_type, opts={})
99
+ prepare_signature_memoization
100
+ memo_sig = existing_sig_for(sig_type)
101
+ return memo_sig if memo_sig
102
+ unless VALID_SIG_TYPES.include?(sig_type)
103
+ raise ArgumentError, error_message_for(sig_type)
104
+ end
105
+
106
+ lk = license_key
107
+ @signature_expiration_for[lk][sig_type] = Time.now + TIME_OFFSET
108
+ @signature_for[lk][sig_type] = RestClient.get(url_for(sig_type, opts))
109
+ end
110
+
111
+ private
112
+
113
+ def error_message_for(sig_type)
114
+ "I don't understand '#{sig_type}'. Please give me one of :ingest, :update, or :view."
115
+ end
116
+
117
+ def existing_sig_for(sig_type)
118
+ return if sig_expired_for?(sig_type)
119
+ @signature_for[license_key][sig_type]
120
+ end
121
+
122
+ def license_key
123
+ @credentials['license_key']
124
+ end
125
+
126
+ def prepare_signature_memoization
127
+ lk = license_key
128
+ @signature_for ||= {}
129
+ @signature_expiration_for ||= {}
130
+ @signature_for[lk] ||= {}
131
+ @signature_expiration_for[lk] ||= {}
132
+ end
133
+
134
+ def sig_expired_for?(sig_type)
135
+ expires_at = @signature_expiration_for[license_key][sig_type]
136
+ return true if expires_at.nil?
137
+ expires_at <= Time.now
138
+ end
139
+
140
+ def url_for(sig_type, opts={})
141
+ contributor, library_id = [:contributor, :library_id].map { |key| opts[key] }
142
+ url = "#{credentials['site']}/api/#{sig_type}_key?licenseKey=#{credentials['license_key']}&duration=#{SIG_DURATION}"
143
+ url += "&contributor=#{contributor}" if contributor
144
+ url += "&library_id=#{library_id}" if library_id
145
+ url
146
+ end
147
+
148
+ end
149
+
150
+ end
@@ -0,0 +1,19 @@
1
+ require 'helix/base'
2
+
3
+ module Helix
4
+
5
+ class Image < Base
6
+
7
+ # The class name, to be used by supporting classes. Such as Config which uses
8
+ # this method as a way to build URLs.
9
+ #
10
+ #
11
+ # @example
12
+ # Helix::Image.media_type_sym #=> :image
13
+ #
14
+ # @return [Symbol] Name of the class.
15
+ def self.media_type_sym; :image; end
16
+
17
+ end
18
+
19
+ end