lacerda 0.3.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/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.markdown +8 -0
- data/Gemfile +2 -0
- data/Guardfile +41 -0
- data/README.md +65 -0
- data/Rakefile +9 -0
- data/circle.yml +3 -0
- data/lacerda.gemspec +35 -0
- data/lib/lacerda.rb +10 -0
- data/lib/lacerda/compare/json_schema.rb +123 -0
- data/lib/lacerda/consume_contract.rb +18 -0
- data/lib/lacerda/consumed_object.rb +16 -0
- data/lib/lacerda/contract.rb +34 -0
- data/lib/lacerda/conversion.rb +132 -0
- data/lib/lacerda/conversion/apiary_to_json_schema.rb +9 -0
- data/lib/lacerda/conversion/data_structure.rb +93 -0
- data/lib/lacerda/conversion/error.rb +7 -0
- data/lib/lacerda/infrastructure.rb +74 -0
- data/lib/lacerda/object_description.rb +34 -0
- data/lib/lacerda/publish_contract.rb +22 -0
- data/lib/lacerda/published_object.rb +14 -0
- data/lib/lacerda/service.rb +63 -0
- data/lib/lacerda/tasks.rb +66 -0
- data/lib/lacerda/version.rb +3 -0
- data/stuff/contracts/author/consume.mson +0 -0
- data/stuff/contracts/author/publish.mson +20 -0
- data/stuff/contracts/edward/consume.mson +7 -0
- data/stuff/contracts/edward/publish.mson +0 -0
- data/stuff/explore/blueprint.apib +239 -0
- data/stuff/explore/swagger.yaml +139 -0
- data/stuff/explore/test.apib +14 -0
- data/stuff/explore/test.mson +239 -0
- data/stuff/explore/tmp.txt +27 -0
- metadata +250 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module Lacerda
|
4
|
+
module Conversion
|
5
|
+
class DataStructure
|
6
|
+
PRIMITIVES = %w{boolean string number array enum object}
|
7
|
+
|
8
|
+
def self.scope(scope, string)
|
9
|
+
[scope, string.to_s].compact.join(Lacerda::SCOPE_SEPARATOR).underscore
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(id, data, scope = nil)
|
13
|
+
@scope = scope
|
14
|
+
@data = data
|
15
|
+
@id = self.class.scope(@scope, id)
|
16
|
+
@schema = json_schema_blueprint
|
17
|
+
@schema['title'] = @id
|
18
|
+
add_description_to_json_schema
|
19
|
+
add_properties_to_json_schema
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_json
|
23
|
+
@schema
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def add_description_to_json_schema
|
29
|
+
return unless @data['sections']
|
30
|
+
description = @data['sections'].select{|d| d['class'] == 'blockDescription' }.first
|
31
|
+
return unless description
|
32
|
+
@schema['description'] = description['content'].strip
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_properties_to_json_schema
|
36
|
+
return unless @data['sections']
|
37
|
+
members = @data['sections'].select{|d| d['class'] == 'memberType' }.first['content'].select{|d| d['class'] == 'property' }
|
38
|
+
members.each do |s|
|
39
|
+
content = s['content']
|
40
|
+
type_definition = content['valueDefinition']['typeDefinition']
|
41
|
+
type = type_definition['typeSpecification']['name']
|
42
|
+
|
43
|
+
spec = {}
|
44
|
+
name = content['name']['literal'].underscore
|
45
|
+
|
46
|
+
# This is either type: primimtive or $ref: reference_name
|
47
|
+
spec.merge!(primitive_or_reference(type))
|
48
|
+
|
49
|
+
# We might have a description
|
50
|
+
spec['description'] = s['description']
|
51
|
+
|
52
|
+
# If it's an array, we need to pluck out the item types
|
53
|
+
if type == 'array'
|
54
|
+
nestedTypes = type_definition['typeSpecification']['nestedTypes']
|
55
|
+
spec['items'] = nestedTypes.map{|t| primitive_or_reference(t) }
|
56
|
+
|
57
|
+
# If it's an object, we need recursion
|
58
|
+
elsif type == 'object'
|
59
|
+
spec['properties'] = {}
|
60
|
+
content['sections'].select{|d| d['class'] == 'memberType'}.each do |data|
|
61
|
+
data_structure = DataStructure.new('tmp', content, @scope).to_json
|
62
|
+
spec['properties'].merge!(data_structure['properties'])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@schema['properties'][name] = spec
|
67
|
+
if attributes = type_definition['attributes']
|
68
|
+
@schema['required'] << name if attributes.include?('required')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def primitive_or_reference(type)
|
74
|
+
return { 'type' => 'object' } if type.blank?
|
75
|
+
|
76
|
+
if PRIMITIVES.include?(type)
|
77
|
+
{ 'type' => type }
|
78
|
+
else
|
79
|
+
{ '$ref' => "#/definitions/#{self.class.scope(@scope, type['literal'])}" }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def json_schema_blueprint
|
84
|
+
{
|
85
|
+
"type" => "object",
|
86
|
+
"properties" => {},
|
87
|
+
"required" => []
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
|
3
|
+
module Lacerda
|
4
|
+
class Infrastructure
|
5
|
+
attr_reader :errors, :data_dir
|
6
|
+
|
7
|
+
def initialize(data_dir:, verbose: false)
|
8
|
+
@verbose = !!verbose
|
9
|
+
@data_dir = data_dir
|
10
|
+
@mutex1 = Mutex.new
|
11
|
+
@mutex2 = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def reload
|
15
|
+
@services = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def contracts_fulfilled?
|
19
|
+
@mutex1.synchronize do
|
20
|
+
@errors = {}
|
21
|
+
publishers.each do |publisher|
|
22
|
+
publisher.satisfies_consumers?(verbose: @verbose)
|
23
|
+
next if publisher.errors.empty?
|
24
|
+
@errors.merge! publisher.errors
|
25
|
+
end
|
26
|
+
@errors.empty?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def publishers
|
31
|
+
services.values.select do |service|
|
32
|
+
service.published_objects.length > 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def consumers
|
37
|
+
services.values.select do |service|
|
38
|
+
service.consumed_objects.length > 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def convert_all!(keep_intermediary_files = false)
|
43
|
+
json_files.each{ |file| FileUtils.rm_f(file) }
|
44
|
+
mson_files.each do |file|
|
45
|
+
Lacerda::Conversion.mson_to_json_schema!(
|
46
|
+
filename: file,
|
47
|
+
keep_intermediary_files: keep_intermediary_files,
|
48
|
+
verbose: @verbose)
|
49
|
+
end
|
50
|
+
reload
|
51
|
+
end
|
52
|
+
|
53
|
+
def mson_files
|
54
|
+
Dir.glob(File.join(@data_dir, "/**/*.mson"))
|
55
|
+
end
|
56
|
+
|
57
|
+
def json_files
|
58
|
+
Dir.glob(File.join(@data_dir, "/**/*.schema.json"))
|
59
|
+
end
|
60
|
+
|
61
|
+
def services
|
62
|
+
@mutex2.synchronize do
|
63
|
+
return @services if @services
|
64
|
+
@services = {}.with_indifferent_access
|
65
|
+
dirs = Dir.glob(File.join(@data_dir, "*/"))
|
66
|
+
dirs.each do |dir|
|
67
|
+
service = Lacerda::Service.new(self, dir)
|
68
|
+
@services[service.name] = service
|
69
|
+
end
|
70
|
+
@services
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# This represents a description of an Object (as it was in MSON and later
|
2
|
+
# JSON Schema). It can come in two flavors:
|
3
|
+
#
|
4
|
+
# 1) A published object
|
5
|
+
# 2) A consumed object
|
6
|
+
#
|
7
|
+
# A published object only refers to one servce:
|
8
|
+
#
|
9
|
+
# - its publisher
|
10
|
+
#
|
11
|
+
# However, a consumed object is referring to two services:
|
12
|
+
#
|
13
|
+
# - its publisher
|
14
|
+
# - its consumer
|
15
|
+
#
|
16
|
+
#
|
17
|
+
module Lacerda
|
18
|
+
class ObjectDescription
|
19
|
+
attr_reader :service, :name, :schema
|
20
|
+
def initialize(defined_in_service, scoped_name, schema)
|
21
|
+
@defined_in_service = defined_in_service
|
22
|
+
@scoped_name = scoped_name
|
23
|
+
@name = remove_service_from_scoped_name(scoped_name)
|
24
|
+
@schema = schema
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def remove_service_from_scoped_name(n)
|
30
|
+
n[n.index(Lacerda::SCOPE_SEPARATOR)+1..-1]
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'lacerda/contract'
|
2
|
+
|
3
|
+
module Lacerda
|
4
|
+
class PublishContract < Lacerda::Contract
|
5
|
+
|
6
|
+
def errors
|
7
|
+
return [] unless @comparator
|
8
|
+
@comparator.errors
|
9
|
+
end
|
10
|
+
|
11
|
+
def satisfies?(consumer)
|
12
|
+
@comparator = Compare::JsonSchema.new(@schema)
|
13
|
+
@comparator.contains?(consumer.consume.scoped_schema(service))
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def object_description_class
|
19
|
+
Lacerda::PublishedObject
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module Lacerda
|
4
|
+
# Models a service and its published objects as well as consumed
|
5
|
+
# objects. The app itself is part of an Infrastructure
|
6
|
+
class Service
|
7
|
+
attr_reader :infrastructure, :publish, :consume, :name, :errors
|
8
|
+
|
9
|
+
def initialize(infrastructure, data_dir)
|
10
|
+
@infrastructure = infrastructure
|
11
|
+
@data_dir = data_dir
|
12
|
+
@name = File.basename(data_dir).underscore
|
13
|
+
load_contracts
|
14
|
+
end
|
15
|
+
|
16
|
+
def consuming_from
|
17
|
+
consumed_objects.map(&:publisher).uniq
|
18
|
+
end
|
19
|
+
|
20
|
+
def consumers
|
21
|
+
infrastructure.services.values.select do |service|
|
22
|
+
service.consuming_from.include?(self)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def consumed_objects(publisher = nil)
|
27
|
+
@consume.objects.select do |o|
|
28
|
+
publisher.blank? or o.publisher == publisher
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def published_objects
|
33
|
+
@publish.objects
|
34
|
+
end
|
35
|
+
|
36
|
+
def satisfies?(service)
|
37
|
+
@publish.satisfies?(service)
|
38
|
+
end
|
39
|
+
|
40
|
+
def satisfies_consumers?(verbose: false)
|
41
|
+
@errors = {}
|
42
|
+
print "#{name.camelize} satisfies: " if verbose
|
43
|
+
consumers.each do |consumer|
|
44
|
+
@publish.satisfies?(consumer)
|
45
|
+
if @publish.errors.empty?
|
46
|
+
print "#{consumer.name.camelize}".green if verbose
|
47
|
+
next
|
48
|
+
end
|
49
|
+
print "#{consumer.name.camelize}".red if verbose
|
50
|
+
@errors["#{name} -> #{consumer.name}"] = @publish.errors
|
51
|
+
end
|
52
|
+
print "\n" if verbose
|
53
|
+
@errors.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def load_contracts
|
59
|
+
@publish = Lacerda::PublishContract.new(self, File.join(@data_dir, "publish.schema.json"))
|
60
|
+
@consume = Lacerda::ConsumeContract.new(self, File.join(@data_dir, "consume.schema.json"))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Lacerda
|
4
|
+
class Tasks
|
5
|
+
include Rake::DSL if defined? Rake::DSL
|
6
|
+
|
7
|
+
def install_tasks
|
8
|
+
namespace :minimum_term do
|
9
|
+
desc "Clean up intermediary json files"
|
10
|
+
task :cleanup do
|
11
|
+
path = File.expand_path("../contracts")
|
12
|
+
files = Dir.glob(File.join(path, "/**/*.schema.json")) +
|
13
|
+
Dir.glob(File.join(path, "/**/*.blueprint-ast.json"))
|
14
|
+
files.each do |file|
|
15
|
+
FileUtils.rm_f(file)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Transform all MSON files in DATA_DIR to JSON Schema using drafter"
|
20
|
+
task :mson_to_json_schema, [:keep_intermediary_files] => :cleanup do |t, args|
|
21
|
+
if ENV['DATA_DIR'].blank?
|
22
|
+
puts "Please set DATA_DIR for me to work in"
|
23
|
+
exit(-1)
|
24
|
+
end
|
25
|
+
|
26
|
+
data_dir = File.expand_path(ENV['DATA_DIR'])
|
27
|
+
unless Dir.exist?(data_dir)
|
28
|
+
puts "Not such directory: #{data_dir}"
|
29
|
+
exit(-1)
|
30
|
+
end
|
31
|
+
|
32
|
+
# For debugging it can be helpful to not clean up the
|
33
|
+
# intermediary blueprint ast files.
|
34
|
+
keep_intermediary_files = args.to_hash.values.include?('keep_intermediary_files')
|
35
|
+
|
36
|
+
# If we were given files, just convert those
|
37
|
+
files = ENV['FILES'].to_s.split(',')
|
38
|
+
|
39
|
+
# OK then, we'll just convert all we find
|
40
|
+
files = Dir.glob(File.join(data_dir, '**/*.mson')) if files.empty?
|
41
|
+
|
42
|
+
# That can't be right
|
43
|
+
if files.empty?
|
44
|
+
puts "No FILES given and nothing found in #{data_dir}"
|
45
|
+
exit(-1)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Let's go
|
49
|
+
puts "Converting #{files.length} files:"
|
50
|
+
|
51
|
+
ok = true
|
52
|
+
files.each do |file|
|
53
|
+
ok = ok && Lacerda::Conversion.mson_to_json_schema(
|
54
|
+
filename: file,
|
55
|
+
keep_intermediary_files: keep_intermediary_files,
|
56
|
+
verbose: true)
|
57
|
+
end
|
58
|
+
|
59
|
+
exit(-1) unless ok
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Lacerda::Tasks.new.install_tasks
|
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Data Structures
|
2
|
+
|
3
|
+
Foo
|
4
|
+
|
5
|
+
# Tag
|
6
|
+
|
7
|
+
Guten Tag
|
8
|
+
|
9
|
+
## Properties
|
10
|
+
- id: 1 (number, required) - Foobar
|
11
|
+
|
12
|
+
# Post
|
13
|
+
|
14
|
+
Explanation for a post
|
15
|
+
|
16
|
+
## Properties
|
17
|
+
- id: 1 (number, required) - The unique identifier for a post
|
18
|
+
- title: Work from home (string, required) - Title of the product
|
19
|
+
- author: 2 (number, optional) - External user id of author
|
20
|
+
- tags: (array[Tag])
|
File without changes
|
@@ -0,0 +1,239 @@
|
|
1
|
+
FORMAT: 1A
|
2
|
+
HOST: http://api.moviepilot.com/
|
3
|
+
|
4
|
+
# Edward
|
5
|
+
|
6
|
+
# Status [/v2]
|
7
|
+
|
8
|
+
We'll forward you to the v2 status resource
|
9
|
+
|
10
|
+
## Retrieve the Entry Point [GET]
|
11
|
+
|
12
|
+
+ Response 200 (application/json; charset=utf-8)
|
13
|
+
+ Body
|
14
|
+
+ Attributes
|
15
|
+
- version: 5.2.1 (string, required) - Version number of currently deployed Edward
|
16
|
+
- server_time: `2015-08-11T08:19:41.556-07:00` (string, required) - Server's time
|
17
|
+
- user_id: 888448 (number, optional) - Requesting user's id
|
18
|
+
|
19
|
+
## Group Posts
|
20
|
+
|
21
|
+
Resources related to posts in the API.
|
22
|
+
|
23
|
+
## Post [/v4/posts/{id}]
|
24
|
+
|
25
|
+
+ Parameters
|
26
|
+
+ id: `3461151` (number, required) - External id
|
27
|
+
|
28
|
+
### View a post [GET]
|
29
|
+
|
30
|
+
+ Response 200 (application/json; charset=utf-8)
|
31
|
+
|
32
|
+
+ Body
|
33
|
+
|
34
|
+
{
|
35
|
+
"id": 3461151,
|
36
|
+
"slug": "it-would-not-have-cost-marketing-any-more-money-to-add-black-widow-on",
|
37
|
+
"title": "It would not have cost marketing any more money to add Black Widow on...",
|
38
|
+
"html_body": "<p>... the cover (or Hawkeye, for that matter). And would it not have been better to take the chance at one more sale by having her on than to lose a sale by not having her on?And don't say that the main characters would then have been smaller because they could have placed Black Widow to the left and behind or above the main characters without changing their relative size.</p>",
|
39
|
+
"total_comments_count": 0,
|
40
|
+
"created_at": "2015-08-11T06:23:50.000-07:00",
|
41
|
+
"questionnaire_id": null,
|
42
|
+
"template": null,
|
43
|
+
"abstract": null,
|
44
|
+
"keywords": null,
|
45
|
+
"cover_image_url": null,
|
46
|
+
"cover_image_caption": null,
|
47
|
+
"seo_title": null,
|
48
|
+
"social_title": null,
|
49
|
+
"social_abstract": null,
|
50
|
+
"suggested_facebook_page_id": "1525889887653500",
|
51
|
+
"comments_disabled": false,
|
52
|
+
"is_mobile_post": false,
|
53
|
+
"legacy_type": "post",
|
54
|
+
"og_image_url": "http://images-cdn.moviepilot.com/image/upload/v1431953404/pocket_post_big2_ct9cex.png",
|
55
|
+
"published_at": "2015-08-11T06:23:50.000-07:00",
|
56
|
+
"first_published_at": "2015-08-11T06:23:50.000-07:00",
|
57
|
+
"last_published_at": "2015-08-11T06:23:50.000-07:00",
|
58
|
+
"promoted": null,
|
59
|
+
"cover_video": {},
|
60
|
+
"view_count": 0,
|
61
|
+
"comment_count": 0,
|
62
|
+
"author": {
|
63
|
+
"id": 1394326,
|
64
|
+
"name": "Richard Lemay",
|
65
|
+
"first_name": "Richard",
|
66
|
+
"last_name": "Lemay",
|
67
|
+
"user_name": null,
|
68
|
+
"image_url": "https://graph.facebook.com/695180451/picture",
|
69
|
+
"description": null,
|
70
|
+
"followers_count": 1,
|
71
|
+
"verified": "contributor",
|
72
|
+
"user_subscription_count": 3,
|
73
|
+
"weekly_readers": 0,
|
74
|
+
"contributions_count": 1,
|
75
|
+
"contribution_view_count": 0,
|
76
|
+
"contribution_comments_count": 0,
|
77
|
+
"facebook_id": "695180451",
|
78
|
+
"twitter_handle": null,
|
79
|
+
"location": null,
|
80
|
+
"avatar_image_url": null,
|
81
|
+
"cover_image_url": null,
|
82
|
+
"provider_image_url": "https://graph.facebook.com/695180451/picture",
|
83
|
+
"flags": [],
|
84
|
+
"roles": [
|
85
|
+
"contributor"
|
86
|
+
],
|
87
|
+
"profile_public": true,
|
88
|
+
"auto_promote_posts": true
|
89
|
+
},
|
90
|
+
"in_reply_to": {
|
91
|
+
"id": 3457913,
|
92
|
+
"type": "post",
|
93
|
+
"first_published_at": "2015-08-10T07:02:09.000-07:00",
|
94
|
+
"last_published_at": "2015-08-10T07:17:08.000-07:00",
|
95
|
+
"slug": "black-widow-was-just-snubbed-by-marvel-again",
|
96
|
+
"title": "Black Widow Was Just Snubbed by Marvel...AGAIN",
|
97
|
+
"author": {
|
98
|
+
"id": 1311215,
|
99
|
+
"name": "Kit Simpson Browne",
|
100
|
+
"user_name": "Kitsb"
|
101
|
+
}
|
102
|
+
},
|
103
|
+
"related_objects": [
|
104
|
+
{
|
105
|
+
"id": 205406,
|
106
|
+
"type": "tag",
|
107
|
+
"slug": "superheroes",
|
108
|
+
"name": "Superheroes",
|
109
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_237,t_mp_quality,w_600/-14cf0c53-33e1-41be-abb6-4490542ec6db.gif",
|
110
|
+
"subscriber_count": 45802
|
111
|
+
},
|
112
|
+
{
|
113
|
+
"id": 932254,
|
114
|
+
"type": "tag",
|
115
|
+
"slug": "marvel",
|
116
|
+
"name": "Marvel",
|
117
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_211,t_mp_quality,w_500/-76cc31d4-897f-486c-9dbb-8e22fd30cf1b.gif",
|
118
|
+
"subscriber_count": 8627
|
119
|
+
},
|
120
|
+
{
|
121
|
+
"id": 958506,
|
122
|
+
"type": "tag",
|
123
|
+
"slug": "casting",
|
124
|
+
"name": "Casting",
|
125
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_400,t_mp_quality,w_960/-364bed3a-81c7-4e44-8735-3500b536e23d.jpg",
|
126
|
+
"subscriber_count": 668
|
127
|
+
},
|
128
|
+
{
|
129
|
+
"id": 958518,
|
130
|
+
"type": "tag",
|
131
|
+
"slug": "opinion",
|
132
|
+
"name": "Opinion",
|
133
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_205,t_mp_quality,w_400/-f08c141c-387b-4dc2-a399-8aadd4084155.gif",
|
134
|
+
"subscriber_count": 236
|
135
|
+
},
|
136
|
+
{
|
137
|
+
"id": 958527,
|
138
|
+
"type": "tag",
|
139
|
+
"slug": "industry",
|
140
|
+
"name": "Industry",
|
141
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_235,t_mp_quality,w_416/-71298c11-8adb-4db0-8375-7d921f732dc2.gif",
|
142
|
+
"subscriber_count": 9
|
143
|
+
},
|
144
|
+
{
|
145
|
+
"id": 958545,
|
146
|
+
"type": "tag",
|
147
|
+
"slug": "rumors",
|
148
|
+
"name": "Rumors",
|
149
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_433,t_mp_quality,w_580/-b44f55e3-0f23-4192-bf77-de110b2f7f9a.gif",
|
150
|
+
"subscriber_count": 178
|
151
|
+
},
|
152
|
+
{
|
153
|
+
"id": 959395,
|
154
|
+
"type": "tag",
|
155
|
+
"slug": "creators",
|
156
|
+
"name": "Creators",
|
157
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_600,t_mp_quality,w_600/-10293491-38f3-4d79-a119-1897cd86c90a.jpg",
|
158
|
+
"subscriber_count": 657
|
159
|
+
},
|
160
|
+
{
|
161
|
+
"id": 959428,
|
162
|
+
"type": "tag",
|
163
|
+
"slug": "news",
|
164
|
+
"name": "News",
|
165
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_450,t_mp_quality,w_960/-e837b84b-712a-4c9a-8663-3117601c7856.jpg",
|
166
|
+
"subscriber_count": 665
|
167
|
+
},
|
168
|
+
{
|
169
|
+
"id": 960923,
|
170
|
+
"type": "tag",
|
171
|
+
"slug": "editorial",
|
172
|
+
"name": "Editorial",
|
173
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_300,t_mp_quality,w_500/-daff61f2-3d14-4c50-ac51-1ece761e6c71.gif",
|
174
|
+
"subscriber_count": 60
|
175
|
+
},
|
176
|
+
{
|
177
|
+
"id": 978148,
|
178
|
+
"type": "tag",
|
179
|
+
"slug": "dvd-blu-ray",
|
180
|
+
"name": "DVD & Blu-ray",
|
181
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_235,t_mp_quality,w_500/-c6dfb789-4d04-4810-a627-5cce20d77e19.gif",
|
182
|
+
"subscriber_count": 27
|
183
|
+
},
|
184
|
+
{
|
185
|
+
"id": 1070824,
|
186
|
+
"type": "tag",
|
187
|
+
"slug": "black-widow",
|
188
|
+
"name": "Black Widow",
|
189
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_380,t_mp_quality,w_920/-ee6baf50-8e27-491a-b2a5-4fbea6edfa2a.jpg",
|
190
|
+
"subscriber_count": 1573
|
191
|
+
},
|
192
|
+
{
|
193
|
+
"id": 1096390,
|
194
|
+
"type": "tag",
|
195
|
+
"slug": "mcu",
|
196
|
+
"name": "Marvel Cinematic Universe",
|
197
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_250,t_mp_quality,w_500/-f71691fc-3123-40ce-8b56-bc14a69f1527.gif",
|
198
|
+
"subscriber_count": 2076
|
199
|
+
},
|
200
|
+
{
|
201
|
+
"id": 1327911,
|
202
|
+
"type": "tag",
|
203
|
+
"slug": "plot",
|
204
|
+
"name": "Plot",
|
205
|
+
"image_url": "http://images-cdn.moviepilot.com/image/upload/c_fill,h_463,t_mp_quality,w_850/-ee8b3c7a-6017-4208-8753-0eeee8ba2c6c.png",
|
206
|
+
"subscriber_count": 3
|
207
|
+
}
|
208
|
+
]
|
209
|
+
}
|
210
|
+
|
211
|
+
|
212
|
+
+ Attributes
|
213
|
+
- id: 3461151 (number, required) - Post's id
|
214
|
+
- slug: `it-would-not-have-cost-marketing-any-more-money-to-add-black-widow-on` (string, required) - Url slug
|
215
|
+
- title: `It would not have cost marketing any more money to add Black Widow on...` (string, required) - Title
|
216
|
+
- html_body: <p>foo</p> (string, required) - Full HTML Body
|
217
|
+
- total_comments_count: 0 (number, required) - Amount of comments
|
218
|
+
- created_at: 2015-08-11T06:23:50.000-07:00 (string, required) - ActiveRecord creation date
|
219
|
+
- questionnaire_id: null (number, optional) - Attached questionaire
|
220
|
+
- template: null (string, optional) - Not sure, perhaps a legacy type?
|
221
|
+
- abstract: null (string, optional) - Not used at the moment, this is from tarantula days
|
222
|
+
- keywords: null (string, optional)
|
223
|
+
- cover_image_url: null (string, optional)
|
224
|
+
- cover_image_caption: null (string, optional)
|
225
|
+
- seo_title: null (string, optional)
|
226
|
+
- social_title: null (string, optional)
|
227
|
+
- social_abstract: null (string, optional)
|
228
|
+
- suggested_facebook_page_id: "1525889887653500" (string, optional)
|
229
|
+
- comments_disabled: false (boolean, optional)
|
230
|
+
- is_mobile_post: false (boolean, optional)
|
231
|
+
- legacy_type: "post" (string, required)
|
232
|
+
- og_image_url: "http://images-cdn.moviepilot.com/image/upload/v1431953404/pocket_post_big2_ct9cex.png" (string, required)
|
233
|
+
- published_at: "2015-08-11T06:23:50.000-07:00" (string, required)
|
234
|
+
- first_published_at: "2015-08-11T06:23:50.000-07:00" (string, required)
|
235
|
+
- last_published_at: "2015-08-11T06:23:50.000-07:00" (string, required)
|
236
|
+
- promoted: null (string, optional)
|
237
|
+
- cover_video: {} (object, optional)
|
238
|
+
- view_count: 0 (number, required)
|
239
|
+
- comment_count: 0 (number, required)
|