minimum-term 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ require 'minimum-term/contract'
2
+
3
+ module MinimumTerm
4
+ class PublishContract < MinimumTerm::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.schema)
14
+ end
15
+
16
+ private
17
+
18
+ def object_description_class
19
+ MinimumTerm::PublishedObject
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ require 'minimum-term/object_description'
2
+
3
+ module MinimumTerm
4
+ class PublishedObject < MinimumTerm::ObjectDescription
5
+
6
+ def publisher
7
+ @defined_in_service
8
+ end
9
+
10
+ def consumer
11
+ @defined_in_service
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ module MinimumTerm
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)
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 = MinimumTerm::PublishContract.new(self, File.join(@data_dir, "publish.schema.json"))
60
+ @consume = MinimumTerm::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 MinimumTerm
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 && MinimumTerm::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
+ MinimumTerm::Tasks.new.install_tasks
@@ -0,0 +1,3 @@
1
+ module MinimumTerm
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,10 @@
1
+ require "minimum-term/conversion"
2
+ require "minimum-term/publish_contract"
3
+ require "minimum-term/consume_contract"
4
+ require "minimum-term/service"
5
+ require "minimum-term/infrastructure"
6
+ require "minimum-term/compare/json_schema"
7
+
8
+ module MinimumTerm
9
+ SCOPE_SEPARATOR = ':'
10
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'minimum-term/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "minimum-term"
8
+ spec.version = MinimumTerm::VERSION
9
+ spec.authors = ["Jannis Hermanns"]
10
+ spec.email = ["jannis@gmail.com"]
11
+
12
+ spec.summary = 'Markdown publish/consume contract parser and validator'
13
+ spec.description = 'Specify which objects your services publish or consume in MSON (markdown) and let this gem validate these contracts.'
14
+ spec.homepage = "https://github.com/moviepilot/minimum-term"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+ spec.license = 'MIT'
21
+
22
+ spec.add_runtime_dependency "activesupport", ["~> 4.2"]
23
+ spec.add_runtime_dependency "rake", ["~> 10.2"]
24
+ spec.add_runtime_dependency "json-schema", ["~> 2.5"]
25
+ spec.add_runtime_dependency "colorize"
26
+
27
+ spec.add_development_dependency "bundler", ["~> 1"]
28
+ spec.add_development_dependency "guard-bundler", ["~> 2.1"]
29
+ spec.add_development_dependency "guard-ctags-bundler", ["~> 1.4"]
30
+ spec.add_development_dependency "guard-rspec", ["~> 4.6"]
31
+ spec.add_development_dependency "rspec", ["~> 3.3"]
32
+ spec.add_development_dependency "simplecov-rcov", ["~> 0.2"]
33
+ end
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])
@@ -0,0 +1,7 @@
1
+ # Data Structures
2
+
3
+ ## Author:Post
4
+
5
+ - title: (string)
6
+ - tag: (object)
7
+ - tagname: (string)
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)
@@ -0,0 +1,139 @@
1
+ # this is an example of the Uber API
2
+ # as a demonstration of an API spec in YAML
3
+ swagger: '2.0'
4
+ info:
5
+ title: Moviepilot.com
6
+ description: Edward
7
+ version: "5.2.1"
8
+ # the domain of the service
9
+ host: api.moviepilot.com
10
+ # array of all schemes that your API supports
11
+ schemes:
12
+ - http
13
+ # will be prefixed to all paths
14
+ basePath: /v4
15
+ produces:
16
+ - application/json
17
+ paths:
18
+ /posts/{id}:
19
+ get:
20
+ summary: Post
21
+ description: |
22
+ Post including author information and so forth
23
+ parameters:
24
+ - name: id
25
+ in: path
26
+ description: External id.
27
+ required: true
28
+ type: number
29
+ format: integer
30
+ tags:
31
+ - Posts
32
+ responses:
33
+ 200:
34
+ description: A post
35
+ schema:
36
+ type: object
37
+ properties:
38
+ id:
39
+ type: number
40
+ slug:
41
+ type: string
42
+ title:
43
+ type: string
44
+ html_body:
45
+ type: string
46
+ total_comments_count:
47
+ type: number
48
+ created_at:
49
+ type: string
50
+ questionnaire_id:
51
+ type: number
52
+ template:
53
+ type: string
54
+ abstract:
55
+ type: string
56
+ keywords:
57
+ type: array
58
+ items:
59
+ type: string
60
+ cover_image_url:
61
+ type: string
62
+ cover_image_caption:
63
+ type: string
64
+ seo_title:
65
+ type: string
66
+ social_title:
67
+ type: string
68
+ social_abstract:
69
+ type: string
70
+ suggested_facebook_page_id:
71
+ type: string
72
+ comments_disabled:
73
+ type: boolean
74
+ is_mobile_post:
75
+ type: boolean
76
+ legacy_type:
77
+ type: string
78
+ og_image_url:
79
+ type: string
80
+ published_at:
81
+ type: string
82
+ first_published_at:
83
+ type: string
84
+ last_published_at:
85
+ type: string
86
+ promoted:
87
+ type: boolean
88
+ cover_video:
89
+ type: string
90
+ view_count:
91
+ type: number
92
+ comment_count:
93
+ type: number
94
+ author:
95
+ type: object
96
+ in_reply_to:
97
+ type: object
98
+ properties:
99
+ id:
100
+ type: number
101
+ type:
102
+ type: string
103
+ first_published_at:
104
+ type: string
105
+ last_published_at:
106
+ type: string
107
+ slug:
108
+ type: string
109
+ title:
110
+ type: string
111
+ author:
112
+ type: object
113
+ properties:
114
+ id:
115
+ type: number
116
+ name:
117
+ type: string
118
+ user_name:
119
+ type: string
120
+ related_objects:
121
+ type: array
122
+ items:
123
+ properties:
124
+ id:
125
+ type: number
126
+ type:
127
+ type: string
128
+ slug:
129
+ type: string
130
+ name:
131
+ type: string
132
+ image_url:
133
+ type: string
134
+ subscriber_count:
135
+ type: number
136
+ required:
137
+ - id
138
+ - title
139
+ - html_body
@@ -0,0 +1,14 @@
1
+ FORMAT: 1A
2
+ HOST: http://api.moviepilot.com/
3
+
4
+ # Edward
5
+
6
+ # Status [/v2]
7
+
8
+ ## GET
9
+
10
+ + Response 200 (application/json)
11
+ + Body
12
+ + Attributes
13
+ - name: Mila (string, required) - Given name
14
+ - email: hello@max-and-mila.com (string, optional) - User's email