apple-news 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +138 -0
- data/Rakefile +6 -0
- data/apple-news.gemspec +28 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/apple-news.rb +41 -0
- data/lib/apple-news/addition.rb +7 -0
- data/lib/apple-news/additions/base.rb +15 -0
- data/lib/apple-news/additions/link.rb +9 -0
- data/lib/apple-news/animation.rb +7 -0
- data/lib/apple-news/animations/appear.rb +7 -0
- data/lib/apple-news/animations/base.rb +17 -0
- data/lib/apple-news/animations/fade_in.rb +8 -0
- data/lib/apple-news/animations/move_in.rb +8 -0
- data/lib/apple-news/animations/scale_fade.rb +8 -0
- data/lib/apple-news/article.rb +33 -0
- data/lib/apple-news/behavior.rb +7 -0
- data/lib/apple-news/behaviors/background_motion.rb +7 -0
- data/lib/apple-news/behaviors/background_parallax.rb +7 -0
- data/lib/apple-news/behaviors/base.rb +15 -0
- data/lib/apple-news/behaviors/motion.rb +7 -0
- data/lib/apple-news/behaviors/parallax.rb +8 -0
- data/lib/apple-news/behaviors/springy.rb +7 -0
- data/lib/apple-news/channel.rb +40 -0
- data/lib/apple-news/component.rb +28 -0
- data/lib/apple-news/component_layout.rb +9 -0
- data/lib/apple-news/components/audio.rb +16 -0
- data/lib/apple-news/components/author.rb +8 -0
- data/lib/apple-news/components/banner_advertisement.rb +14 -0
- data/lib/apple-news/components/base.rb +17 -0
- data/lib/apple-news/components/body.rb +8 -0
- data/lib/apple-news/components/byline.rb +8 -0
- data/lib/apple-news/components/caption.rb +8 -0
- data/lib/apple-news/components/chapter.rb +10 -0
- data/lib/apple-news/components/container.rb +8 -0
- data/lib/apple-news/components/divider.rb +8 -0
- data/lib/apple-news/components/embed_web_video.rb +12 -0
- data/lib/apple-news/components/figure.rb +8 -0
- data/lib/apple-news/components/gallery.rb +8 -0
- data/lib/apple-news/components/header.rb +7 -0
- data/lib/apple-news/components/heading.rb +8 -0
- data/lib/apple-news/components/illustrator.rb +8 -0
- data/lib/apple-news/components/image.rb +14 -0
- data/lib/apple-news/components/instagram.rb +9 -0
- data/lib/apple-news/components/intro.rb +8 -0
- data/lib/apple-news/components/logo.rb +8 -0
- data/lib/apple-news/components/medium_rectangle_advertisement.rb +7 -0
- data/lib/apple-news/components/mosaic.rb +8 -0
- data/lib/apple-news/components/music.rb +8 -0
- data/lib/apple-news/components/photo.rb +8 -0
- data/lib/apple-news/components/photographer.rb +8 -0
- data/lib/apple-news/components/portrait.rb +8 -0
- data/lib/apple-news/components/pullquote.rb +8 -0
- data/lib/apple-news/components/quote.rb +8 -0
- data/lib/apple-news/components/scalable_image.rb +14 -0
- data/lib/apple-news/components/section.rb +8 -0
- data/lib/apple-news/components/text.rb +13 -0
- data/lib/apple-news/components/title.rb +8 -0
- data/lib/apple-news/components/tweet.rb +9 -0
- data/lib/apple-news/components/video.rb +12 -0
- data/lib/apple-news/configuration.rb +12 -0
- data/lib/apple-news/document.rb +22 -0
- data/lib/apple-news/document/attachments.rb +24 -0
- data/lib/apple-news/document/metadata.rb +43 -0
- data/lib/apple-news/document/persistence.rb +42 -0
- data/lib/apple-news/layout.rb +8 -0
- data/lib/apple-news/links.rb +19 -0
- data/lib/apple-news/metadata.rb +15 -0
- data/lib/apple-news/multipart_post_patch.rb +29 -0
- data/lib/apple-news/properties.rb +87 -0
- data/lib/apple-news/properties/advertising_layout.rb +7 -0
- data/lib/apple-news/properties/advertising_settings.rb +7 -0
- data/lib/apple-news/properties/anchor.rb +15 -0
- data/lib/apple-news/properties/base.rb +7 -0
- data/lib/apple-news/properties/caption_descriptor.rb +8 -0
- data/lib/apple-news/properties/color_stop.rb +8 -0
- data/lib/apple-news/properties/content_inset.rb +7 -0
- data/lib/apple-news/properties/gallery_item.rb +7 -0
- data/lib/apple-news/properties/margin.rb +7 -0
- data/lib/apple-news/properties/offset.rb +7 -0
- data/lib/apple-news/property.rb +7 -0
- data/lib/apple-news/request.rb +6 -0
- data/lib/apple-news/requests/get.rb +28 -0
- data/lib/apple-news/requests/post.rb +57 -0
- data/lib/apple-news/resource.rb +25 -0
- data/lib/apple-news/scene.rb +7 -0
- data/lib/apple-news/scenes/base.rb +15 -0
- data/lib/apple-news/scenes/fading_sticky_header.rb +8 -0
- data/lib/apple-news/scenes/parallax_scale_header.rb +7 -0
- data/lib/apple-news/section.rb +27 -0
- data/lib/apple-news/security.rb +46 -0
- data/lib/apple-news/style.rb +10 -0
- data/lib/apple-news/styles/base.rb +7 -0
- data/lib/apple-news/styles/border.rb +7 -0
- data/lib/apple-news/styles/component.rb +7 -0
- data/lib/apple-news/styles/component_text.rb +9 -0
- data/lib/apple-news/styles/document.rb +7 -0
- data/lib/apple-news/styles/drop_cap.rb +9 -0
- data/lib/apple-news/styles/fill.rb +11 -0
- data/lib/apple-news/styles/gradient_fill.rb +12 -0
- data/lib/apple-news/styles/image_fill.rb +11 -0
- data/lib/apple-news/styles/inline_text.rb +7 -0
- data/lib/apple-news/styles/linear_gradient_fill.rb +9 -0
- data/lib/apple-news/styles/shadow.rb +8 -0
- data/lib/apple-news/styles/stroke.rb +7 -0
- data/lib/apple-news/styles/text.rb +8 -0
- data/lib/apple-news/styles/text_stroke.rb +8 -0
- data/lib/apple-news/styles/video_fill.rb +12 -0
- data/lib/apple-news/version.rb +3 -0
- metadata +241 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
module AppleNews
|
2
|
+
module Component
|
3
|
+
class Video < Base
|
4
|
+
role "video"
|
5
|
+
required_property :url
|
6
|
+
optional_properties :accessibility_caption, :aspect_ratio, :explicit_content, :still_url
|
7
|
+
|
8
|
+
property_inflection :url, 'URL'
|
9
|
+
property_inflection :still_url, 'stillURL'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
require 'apple-news/document/attachments'
|
4
|
+
require 'apple-news/document/metadata'
|
5
|
+
require 'apple-news/document/persistence'
|
6
|
+
|
7
|
+
module AppleNews
|
8
|
+
class Document
|
9
|
+
include Attachments
|
10
|
+
include Metadata
|
11
|
+
include Persistence
|
12
|
+
include Resource
|
13
|
+
|
14
|
+
attr_accessor :article, :metadata
|
15
|
+
|
16
|
+
def initialize(article = nil, metadata = {})
|
17
|
+
@article = article
|
18
|
+
@metadata = {}
|
19
|
+
@files = {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AppleNews
|
2
|
+
class Document
|
3
|
+
module Attachments
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def add_file(file, mime = nil)
|
8
|
+
filename = Pathname.new(file).basename.to_s
|
9
|
+
mime ||= MIME::Types.type_for(filename)
|
10
|
+
|
11
|
+
@files[filename] = UploadIO.new(file, mime, filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_file_at_path(path)
|
15
|
+
add_file(File.new(path))
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_string_as_file(name, contents, type)
|
19
|
+
@files[name] = UploadIO.new(StringIO.new(contents), type, name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AppleNews
|
2
|
+
class Document
|
3
|
+
module Metadata
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def is_sponsored?
|
8
|
+
@metadata['isSponsored']
|
9
|
+
end
|
10
|
+
alias_method :sponsored?, :is_sponsored?
|
11
|
+
|
12
|
+
def is_sponsored=(val)
|
13
|
+
@metadata['isSponsored'] = val
|
14
|
+
end
|
15
|
+
|
16
|
+
def is_preview?
|
17
|
+
@metadata['isPreview']
|
18
|
+
end
|
19
|
+
alias_method :preview?, :is_preview?
|
20
|
+
|
21
|
+
def is_preview=(val)
|
22
|
+
@metadata['isPreview'] = val
|
23
|
+
end
|
24
|
+
|
25
|
+
def accessory_text
|
26
|
+
@metadata['accessoryText']
|
27
|
+
end
|
28
|
+
|
29
|
+
def accessory_text=(val)
|
30
|
+
@metadata['accessoryText'] = val
|
31
|
+
end
|
32
|
+
|
33
|
+
def links
|
34
|
+
@metadata['links']
|
35
|
+
end
|
36
|
+
|
37
|
+
def links=(val)
|
38
|
+
@metadata['links'] = val
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AppleNews
|
2
|
+
class Document
|
3
|
+
module Persistence
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
def save!
|
8
|
+
request = AppleNews::Request::Post.new(endpoint_url)
|
9
|
+
request.fields = {
|
10
|
+
'metadata' => metadata_field,
|
11
|
+
'article.json' => article_json
|
12
|
+
}.merge(@files)
|
13
|
+
|
14
|
+
resp = request.call
|
15
|
+
article.update_with_data(resp['data'].delete('document'))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def endpoint_url
|
21
|
+
if article.persisted?
|
22
|
+
"/articles/#{id}"
|
23
|
+
else
|
24
|
+
"/channels/#{AppleNews.config.channel_id}/articles"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def metadata_field
|
29
|
+
JSON.dump({ data: @metadata })
|
30
|
+
end
|
31
|
+
|
32
|
+
def article_json
|
33
|
+
UploadIO.new(
|
34
|
+
StringIO.new(JSON.dump(article.as_json)),
|
35
|
+
"application/json",
|
36
|
+
"article.json"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AppleNews
|
2
|
+
module Links
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
def link(name)
|
7
|
+
URI::parse(@links[name]).path
|
8
|
+
end
|
9
|
+
|
10
|
+
def section_link_id(name)
|
11
|
+
link(name).match(/\/sections\/([A-Za-z0-9\-]+)/)[1]
|
12
|
+
end
|
13
|
+
|
14
|
+
def channel_link_id(name)
|
15
|
+
link(name).match(/\/channels\/([A-Za-z0-9\-]+)/)[1]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AppleNews
|
2
|
+
class Metadata
|
3
|
+
include Properties
|
4
|
+
optional_properties :authors, :campaign_data, :canonical_url, :date_created,
|
5
|
+
:date_modified, :date_published, :excerpt, :generator_identifier,
|
6
|
+
:keywords, :thumbnail_url
|
7
|
+
|
8
|
+
optional_property :generator_name, "apple-news-rb"
|
9
|
+
optional_property :generator_version, AppleNews::VERSION
|
10
|
+
optional_property :transparent_toolbar, false
|
11
|
+
|
12
|
+
property_inflection :canonical_url, 'canonicalURL'
|
13
|
+
property_inflection :thumbnail_url, 'thumbnailURL'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# This is really shitty and no one should ever monkey patch, but the Apple News API
|
2
|
+
# requires that the size of the form part should be specified in the Content-Disposition
|
3
|
+
# header, and not with the Content-Length header. I'm not sure if size is even a valid param
|
4
|
+
# in the Content-Disposition header, but here we are.
|
5
|
+
module Parts
|
6
|
+
class FilePart
|
7
|
+
def build_head(boundary, name, filename, type, content_len, opts = {}, headers = {})
|
8
|
+
trans_encoding = opts["Content-Transfer-Encoding"] || "binary"
|
9
|
+
content_disposition = opts["Content-Disposition"] || "form-data"
|
10
|
+
|
11
|
+
part = ''
|
12
|
+
part << "--#{boundary}\r\n"
|
13
|
+
part << "Content-Disposition: #{content_disposition}; name=#{name}; filename=\"#{filename}\"; size=#{content_len}\r\n"
|
14
|
+
part << "Content-Length: #{content_len}\r\n"
|
15
|
+
if content_id = opts["Content-ID"]
|
16
|
+
part << "Content-ID: #{content_id}\r\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
if headers["Content-Type"] != nil
|
20
|
+
part << "Content-Type: " + headers["Content-Type"] + "\r\n"
|
21
|
+
else
|
22
|
+
part << "Content-Type: #{type}\r\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
part << "Content-Transfer-Encoding: #{trans_encoding}\r\n"
|
26
|
+
part << "\r\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module AppleNews
|
2
|
+
module Properties
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def initialize(opts = nil)
|
6
|
+
if !opts.nil?
|
7
|
+
opts = ActiveSupport::HashWithIndifferentAccess.new(opts)
|
8
|
+
self.class.properties.each do |prop, default|
|
9
|
+
instance_variable_set "@#{prop}", opts.fetch(prop, default)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
class_attribute :_required_property_map
|
16
|
+
class_attribute :_optional_property_map
|
17
|
+
class_attribute :_property_inflection
|
18
|
+
|
19
|
+
self._required_property_map ||= {}
|
20
|
+
self._optional_property_map ||= {}
|
21
|
+
self._property_inflection ||= {}
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
_required_property_map.each do |prop, _|
|
25
|
+
return false if send(prop).nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def properties
|
32
|
+
self.class.properties
|
33
|
+
end
|
34
|
+
|
35
|
+
def as_json
|
36
|
+
Hash[properties.map { |key, _|
|
37
|
+
json_key = _property_inflection[key] || key.to_s.camelize(:lower)
|
38
|
+
val = if send(key).respond_to?(:as_json)
|
39
|
+
send(key).as_json
|
40
|
+
elsif send(key).is_a?(Array)
|
41
|
+
send(key).map(&:as_json)
|
42
|
+
elsif send(key).is_a?(Hash)
|
43
|
+
Hash[send(key).map {|k, v| [k.to_s, v.as_json]}]
|
44
|
+
else
|
45
|
+
send(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
[json_key, val]
|
49
|
+
}.reject { |p| p[1].nil? }]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def inherited(base)
|
55
|
+
super
|
56
|
+
base._required_property_map = _required_property_map.dup
|
57
|
+
base._optional_property_map = _optional_property_map.dup
|
58
|
+
end
|
59
|
+
|
60
|
+
def required_properties(*args)
|
61
|
+
args.each { |arg| required_property(arg) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def required_property(name, default = nil)
|
65
|
+
_required_property_map[name] = default
|
66
|
+
attr_accessor name
|
67
|
+
end
|
68
|
+
|
69
|
+
def optional_properties(*args)
|
70
|
+
args.each { |arg| optional_property(arg) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def optional_property(name, default = nil)
|
74
|
+
_optional_property_map[name] = default
|
75
|
+
attr_accessor name
|
76
|
+
end
|
77
|
+
|
78
|
+
def property_inflection(name, inflection)
|
79
|
+
_property_inflection[name] = inflection
|
80
|
+
end
|
81
|
+
|
82
|
+
def properties
|
83
|
+
_required_property_map.merge(_optional_property_map)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AppleNews
|
2
|
+
module Property
|
3
|
+
class Anchor < Base
|
4
|
+
required_property :target_anchor_position
|
5
|
+
optional_properties :origin_anchor_position, :range_length, :range_start,
|
6
|
+
:target_component_identifier
|
7
|
+
|
8
|
+
def valid?
|
9
|
+
super && [
|
10
|
+
'top', 'center', 'bottom'
|
11
|
+
].include?(target_anchor_position)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|