lacerda 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff23a2681e40e382e4e01fb4ce0b67aaab1c24ce
4
- data.tar.gz: 605bd530f4bc51914bbc2f6e513156e4e7eedc20
3
+ metadata.gz: 13bff5451aa612adedb40d0d20a26d6826796546
4
+ data.tar.gz: 4051b656fe279dea8381b73351b4dbc7a55d0cb8
5
5
  SHA512:
6
- metadata.gz: 131589ccbb3c6ff9ada1e8498ce3836ad151eaa0cb778675086c67f23f5b3e36494c8914f7bcbf041156f4c0b49d23738ef8f6a424e186b1504fa5310fe4b9e4
7
- data.tar.gz: d7d6ae3c7d5506772fe8eb787dc73365b8fa1337afc36977cf5685936007291f7aa9232da56512904bdf679e6f883cf07ee65f923959376c0a948aee35621c06
6
+ metadata.gz: d80147036af1a6424d731c34141d670519996363e3e1234ee5ee6e05393581bf0f07e63ebe6b883e2864851ffd83264ba4e18e26f68041b3cdeb4e385e660298
7
+ data.tar.gz: fb3ca9e05a706d6bae08d0461f5b6c319724120cb385af737bc25771eb8721934f950a58f76d29abdf066942b5a47e595211cacf25b785a3ff03ac9c98225674
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,14 @@
1
+ # 0.12.0 (06-Nov-15)
2
+ - add Lacerda::Service#publishes?(object_name)
3
+ - add Lacerda::Service#consumes?(object_name)
4
+ - add Lacerda::Service#consumes_from?(service_name, object_name)
5
+ - add Lacerda::Service#consume_object_from(service_name, object_name)
6
+ - load Lacerda::VERSION
7
+ - add missing json-schema require
8
+ - fix a typo in the ERR_MISSING_DEFINITION error message
9
+ - update blumquist
10
+ - allow top level types without a publishing service prefix in consume specifications
11
+
1
12
  # 0.11.0 (04-Nov-15)
2
13
  - rename ConsumeContract and PublishContract to ConsumeSpecification and PublishSpecification
3
14
  - omit redundant service name in model names of publish contracts
data/Guardfile CHANGED
@@ -27,14 +27,10 @@ guard 'ctags-bundler', :src_path => ["lib"] do
27
27
  watch(/^.+\.gemspec/)
28
28
  end
29
29
 
30
- guard :rspec, cmd: 'IGNORE_LOW_COVERAGE=1 rspec', all_on_start: true do
30
+ guard :rspec, cmd: 'rspec', all_on_start: true do
31
31
  watch(%r{^spec/support/.*\.mson$}) { "spec" }
32
32
  watch(%r{^spec/support/.*\.rb$}) { "spec" }
33
33
  watch('spec/spec_helper.rb') { "spec" }
34
-
35
- # We could run individual specs, sure, but for now I dictate the tests
36
- # are only green when we have 100% coverage, so partial runs will never
37
- # succeed. Therefore, always run all the things.
38
34
  watch(%r{^(spec/.+_spec\.rb)$}) { "spec" }
39
35
  watch(%r{^lib/(.+)\.rb$}) { "spec" }
40
36
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Lacerda [![Circle CI](https://circleci.com/gh/moviepilot/lacerda/tree/master.svg?style=svg)](https://circleci.com/gh/moviepilot/lacerda/tree/master) [![Coverage Status](https://coveralls.io/repos/moviepilot/lacerda/badge.svg?branch=master&service=github)](https://coveralls.io/github/moviepilot/lacerda?branch=master) [![Code Climate](https://codeclimate.com/github/moviepilot/lacerda/badges/gpa.svg)](https://codeclimate.com/github/moviepilot/lacerda) [![Dependency Status](https://gemnasium.com/moviepilot/lacerda.svg)](https://gemnasium.com/moviepilot/lacerda)
1
+ # Lacerda [![Circle CI](https://circleci.com/gh/moviepilot/lacerda/tree/master.svg?style=shield)](https://circleci.com/gh/moviepilot/lacerda/tree/master) [![Coverage Status](https://coveralls.io/repos/moviepilot/lacerda/badge.svg?branch=master&service=github)](https://coveralls.io/github/moviepilot/lacerda?branch=master) [![Code Climate](https://codeclimate.com/github/moviepilot/lacerda/badges/gpa.svg)](https://codeclimate.com/github/moviepilot/lacerda) [![Dependency Status](https://gemnasium.com/moviepilot/lacerda.svg)](https://gemnasium.com/moviepilot/lacerda) [![Gem Version](https://badge.fury.io/rb/lacerda.svg)](https://badge.fury.io/rb/lacerda)
2
2
 
3
3
  ![](https://dl.dropboxusercontent.com/u/1953503/lacerda.jpg)
4
4
  > «No, no -- we have to go on. We need total coverage.»<sup>[1](#references)</sup>
@@ -13,18 +13,15 @@ This gem can:
13
13
  - which service consumes what from which other service
14
14
  - if all services consume and publish conforming to their contracts.
15
15
 
16
- You likely don't want to use it on its own but head on over to the [Zeta](https://github.com/moviepilot/zeta) gem which explains things in more detail. If you're just looking for ONE way to transform MSON files into JSON Schema, read on:
16
+ You likely **don't want to use it on its own** but integrate your infrastructure via
17
17
 
18
- ## Getting started
19
- First, check out [this API Blueprint map](https://github.com/apiaryio/api-blueprint/wiki/API-Blueprint-Map) to understand how _API Blueprint_ documents are laid out:
18
+ ⏩[Zeta](https://github.com/moviepilot/zeta)
20
19
 
21
- ![API Blueprint map](https://raw.githubusercontent.com/apiaryio/api-blueprint/master/assets/map.png)
20
+ . Click the link, it will explains things in more detail. If you're just looking for *one* way to transform MSON files into JSON Schema, read on:
22
21
 
23
- You can see that their structure covers a full API use case with resource groups, single resources, actions on those resources including requests and responses. All we want, though, is the little red top level branch called `Data structures`.
24
-
25
- We're using a ruby gem called [RedSnow](https://github.com/apiaryio/redsnow), which has bindings to [SnowCrash](https://github.com/apiaryio/snowcrash) which parses _API Blueprints_ into an AST.
26
-
27
- Luckily, a rake task does all that for you. To convert all `*.mson` files in `contracts/` into `*.schema.json` files,
22
+ ## MSON to JSON schema
23
+ *Lacerda* offers a rake task that converts MSON files into JSON schemas.
24
+ To convert all `*.mson` files in `contracts/` into `*.schema.json` files,
28
25
 
29
26
  put this in your `Rakefile`:
30
27
 
@@ -44,17 +41,10 @@ OK /home/dev/lacerda/specifications/publisher/publish.mson
44
41
  /home/dev/lacerda$
45
42
  ```
46
43
 
47
- ## Tests and development
48
- - run `bundle` once
49
- - run `guard` in a spare terminal which will run the tests,
50
- install gems, and so forth
51
- - run `rspec spec` to run all the tests
52
- - check out `open coverage/index.html` or `open coverage/rcov/index.html`
53
- - run `bundle console` to play around with a console
54
-
55
44
  ## Structure
56
45
 
57
- By converting all files in a directory this gem will build up the following relationships:
46
+ By loading all files in a directory this gem will build up the following
47
+ relationships:
58
48
 
59
49
  - Infrastructure
60
50
  - Service
@@ -64,6 +54,30 @@ By converting all files in a directory this gem will build up the following rela
64
54
  - Consume specification
65
55
  - ConsumedObjects
66
56
 
67
- # References
57
+ ## Compatibility
58
+
59
+ Until there is a native MSON to JSON schema parser available, we do the
60
+ conversion ourselves. These features from the MSON specification are currently supported:
68
61
 
62
+ - [x] primitive properties: `string`, `number`, `boolean`, `null`
63
+ - [x] `object` properties
64
+ - [x] `array` properties with items of one type
65
+ - [ ] `array` properties of mixed types
66
+ - [ ] `array` properties of arrays
67
+ - [ ] `enum` properties
68
+ - [ ] `One of` properties mutually exclusive properties
69
+ - [x] `Referencing`
70
+ - [ ] `Mixins`
71
+ - [ ] Variable property names
72
+
73
+
74
+ ## Tests and development
75
+ - run `bundle` once
76
+ - run `guard` in a spare terminal which will run the tests,
77
+ install gems, and so forth
78
+ - run `rspec spec` to run all the tests
79
+ - check out `open coverage/index.html` or `open coverage/rcov/index.html`
80
+ - run `bundle console` to play around with a console
81
+
82
+ # References
69
83
  [1] This quote in French quotation marks is from "Fear and Loathing in Las Vegas". Since I can't link to the book, a link to the [movie script](http://www.dailyscript.com/scripts/fearandloathing.html) shall suffice.
data/lacerda.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency "json-schema", ["~> 2.5"]
25
25
  spec.add_runtime_dependency "redsnow", ["~> 0.4"]
26
26
  spec.add_runtime_dependency "colorize"
27
- spec.add_runtime_dependency "blumquist", ["~> 0.2"]
27
+ spec.add_runtime_dependency "blumquist", ["~> 0.3"]
28
28
 
29
29
  spec.add_development_dependency "bundler", ["~> 1"]
30
30
  spec.add_development_dependency "guard-bundler", ["~> 2.1"]
@@ -33,4 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "rspec", ["~> 3.3"]
34
34
  spec.add_development_dependency "coveralls", ["~> 0.8"]
35
35
  spec.add_development_dependency "codeclimate-test-reporter"
36
+ spec.add_development_dependency 'pry'
37
+ spec.add_development_dependency 'pry-rescue'
38
+ spec.add_development_dependency 'pry-stack_explorer'
36
39
  end
data/lib/lacerda.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'lacerda/version'
1
2
  require 'lacerda/conversion'
2
3
  require 'lacerda/publish_specification'
3
4
  require 'lacerda/consume_specification'
@@ -7,6 +8,7 @@ require 'lacerda/compare/json_schema'
7
8
  require 'lacerda/reporter'
8
9
  require 'lacerda/reporters/multi'
9
10
  require 'lacerda/reporters/stdout'
11
+ require 'active_support/core_ext/string'
10
12
 
11
13
  module Lacerda
12
14
  SCOPE_SEPARATOR = '::'
@@ -24,4 +26,9 @@ module Lacerda
24
26
  .underscore
25
27
  .gsub(/:/, SCOPE_SEPARATOR)
26
28
  end
29
+
30
+ # Poor man's deep copy: json 🆗 🆒
31
+ def self.deep_copy(object)
32
+ JSON.parse({m: object}.to_json)['m']
33
+ end
27
34
  end
@@ -3,7 +3,7 @@ module Lacerda
3
3
  class JsonSchema
4
4
  ERRORS = {
5
5
  :ERR_ARRAY_ITEM_MISMATCH => "The items in the published array don't match the consumer's specification.",
6
- :ERR_MISSING_DEFINITION => "The publish specification is missing a type defined in esothe consumer's specification.",
6
+ :ERR_MISSING_DEFINITION => "The publish specification is missing a type defined in the consumer's specification.",
7
7
  :ERR_MISSING_POINTER => "A JSON pointer could not be resolved.",
8
8
  :ERR_MISSING_PROPERTY => "The published object is missing a property required by your specification.",
9
9
  :ERR_MISSING_REQUIRED => "The published object has an optional property that you marked as required in your specification.",
@@ -22,18 +22,27 @@ module Lacerda
22
22
  @errors = []
23
23
  @initial_location = initial_location
24
24
  @contained_schema = contained_schema
25
- definitions_contained?
25
+ properties_contained?
26
26
  end
27
27
 
28
28
  private
29
29
 
30
- def definitions_contained?
31
- @contained_schema['definitions'].each do |property, contained_property|
32
- containing_property = @containing_schema['definitions'][property]
30
+ def properties_contained?
31
+ @contained_schema['properties'].each do |property, contained_property|
32
+ resolved_contained_property = data_for_pointer(property, @contained_schema)
33
+ containing_property = @containing_schema['properties'][property]
33
34
  if !containing_property
34
35
  _e(:ERR_MISSING_DEFINITION, [@initial_location, property])
35
36
  else
36
- schema_contains?(containing_property, contained_property, [property])
37
+ resolved_containing_property = data_for_pointer(
38
+ containing_property,
39
+ @containing_schema
40
+ )
41
+ schema_contains?(
42
+ resolved_containing_property,
43
+ resolved_contained_property,
44
+ [property]
45
+ )
37
46
  end
38
47
  end
39
48
  @errors.empty?
@@ -49,7 +58,7 @@ module Lacerda
49
58
 
50
59
  # We can only compare types and $refs, so let's make
51
60
  # sure they're there
52
- return _e(:ERR_MISSING_TYPE_AND_REF) unless
61
+ return _e(:ERR_MISSING_TYPE_AND_REF, location) unless
53
62
  (consume['type'] or consume['$ref']) and
54
63
  (publish['type'] or publish['$ref'])
55
64
 
@@ -117,6 +126,36 @@ module Lacerda
117
126
  true
118
127
  end
119
128
 
129
+ # Resolve pointer data idempotent(ally?). It will resolve
130
+ #
131
+ # "foobar"
132
+ #
133
+ # or
134
+ #
135
+ # { "$ref": "#/definitions/foobar" }
136
+ #
137
+ # or
138
+ #
139
+ # { "type": "whatever", ... }
140
+ #
141
+ # to
142
+ #
143
+ # { "type" :"whatever", ... }
144
+ #
145
+ def data_for_pointer(data_or_pointer, schema)
146
+ data = nil
147
+ if data_or_pointer['type']
148
+ data = data_or_pointer
149
+ elsif pointer = data_or_pointer['$ref']
150
+ data = resolve_pointer(pointer, schema)
151
+ else
152
+ data = schema['definitions'][data_or_pointer]
153
+ end
154
+ data
155
+ end
156
+
157
+ # Looks up a pointer like #/definitions/foobar and return
158
+ # its definition
120
159
  def resolve_pointer(pointer, schema)
121
160
  type = pointer[/\#\/definitions\/([^\/]+)$/, 1]
122
161
  return false unless type
@@ -1,7 +1,34 @@
1
+ require 'json'
1
2
  require 'lacerda/specification'
2
3
 
3
4
  module Lacerda
4
5
  class ConsumeSpecification < Lacerda::Specification
6
+
7
+ # We will remove any properties from the schema that are not referencing
8
+ # a published object from another service, but that are just local definitions
9
+ # i.e.
10
+ #
11
+ # {
12
+ # "type": "object",
13
+ # "definitions": {
14
+ # "message_properties": { ...},
15
+ # "service::message": {...}
16
+ # "
17
+ # }
18
+ # "properties": {
19
+ # "message_properties": { "$ref": "#/message_properties }, <- remove
20
+ # "service::message": { "$ref": "#/service::message" } <- keep
21
+ # }
22
+ # }
23
+ #
24
+ def initialize(service, schema_or_file)
25
+ super
26
+ return unless @schema['properties']
27
+ @schema['properties'] = @schema['properties'].keep_if do |k, v|
28
+ !!k.index(Lacerda::SCOPE_SEPARATOR)
29
+ end
30
+ end
31
+
5
32
  def object_description_class
6
33
  Lacerda::ConsumedObject
7
34
  end
@@ -9,19 +36,28 @@ module Lacerda
9
36
  def scoped_schema(service)
10
37
  service_name_prefix = Lacerda.underscore(service.name + Lacerda::SCOPE_SEPARATOR)
11
38
 
12
- # Poor man's deep clone: json 🆗 🆒
13
- filtered_schema = JSON.parse(schema.to_json)
14
- filtered_schema['definitions'].select! do |k|
39
+ filtered_schema = Lacerda.deep_copy(schema)
40
+ filtered_schema['properties'].select! do |k|
15
41
  Lacerda.underscore(k).start_with?(service_name_prefix)
16
42
  end
17
43
  filtered_schema
18
44
  end
19
45
 
46
+ def object?(name)
47
+ underscored_name = Lacerda.underscore(name)
48
+ !!@schema[:definitions][underscored_name]
49
+ end
50
+
20
51
  def object(name)
21
52
  underscored_name = Lacerda.underscore(name)
22
- schema = @schema[:definitions][underscored_name]
23
- raise Lacerda::Service::InvalidObjectTypeError.new(underscored_name) unless schema
24
- Lacerda::ConsumedObject.new(service, name, schema)
53
+ object_schema = Lacerda.deep_copy(@schema[:definitions][underscored_name])
54
+ raise Lacerda::Service::InvalidObjectTypeError.new("Invalid object type: #{underscored_name.to_s.to_json}") unless object_schema
55
+
56
+ # Copy the definitions of our schema into the schema for the
57
+ # object in case its properties include json pointers
58
+ object_schema[:definitions] = Lacerda.deep_copy(@schema[:definitions])
59
+
60
+ Lacerda::ConsumedObject.new(service, name, object_schema)
25
61
  end
26
62
  end
27
63
  end
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/string'
2
1
 
3
2
  module Lacerda
4
3
  module Conversion
@@ -1,3 +1,6 @@
1
+ require 'json-schema'
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+
1
4
  # This represents a description of an Object (as it was in MSON and later
2
5
  # JSON Schema). It can come in two flavors:
3
6
  #
@@ -21,7 +24,7 @@ module Lacerda
21
24
  @defined_in_service = defined_in_service
22
25
  @scoped_name = scoped_name
23
26
  @name = remove_service_from_scoped_name(scoped_name)
24
- @schema = schema
27
+ @schema = schema.with_indifferent_access
25
28
  end
26
29
 
27
30
  def validate_data!(data)
@@ -20,14 +20,13 @@ module Lacerda
20
20
  result
21
21
  end
22
22
 
23
- def object(name)
24
- scoped_name = Lacerda.underscore(name.to_s)
25
-
26
- # Add our own prefix automatically if necessary
27
- unless scoped_name.start_with?(Lacerda.underscore(service.name))
28
- scoped_name = [Lacerda.underscore(service.name), scoped_name].join(Lacerda::SCOPE_SEPARATOR)
29
- end
23
+ def object?(name)
24
+ scoped_name = scopify_name(name)
25
+ !!@schema[:definitions][scoped_name]
26
+ end
30
27
 
28
+ def object(name)
29
+ scoped_name = scopify_name(name)
31
30
  schema = @schema[:definitions][scoped_name]
32
31
  raise Lacerda::Service::InvalidObjectTypeError.new(scoped_name) unless schema
33
32
  Lacerda::PublishedObject.new(service, scoped_name, schema)
@@ -35,6 +34,14 @@ module Lacerda
35
34
 
36
35
  private
37
36
 
37
+ def scopify_name(name)
38
+ scoped_name = Lacerda.underscore(name.to_s)
39
+
40
+ # Add our own prefix automatically if necessary
41
+ return scoped_name if scoped_name.start_with?(Lacerda.underscore(service.name))
42
+ [Lacerda.underscore(service.name), scoped_name].join(Lacerda::SCOPE_SEPARATOR)
43
+ end
44
+
38
45
  def object_description_class
39
46
  Lacerda::PublishedObject
40
47
  end
@@ -28,10 +28,23 @@ module Lacerda
28
28
 
29
29
  def consumed_objects(publisher = nil)
30
30
  @consume.objects.select do |o|
31
+ next if o.publisher_name.blank?
31
32
  publisher.blank? or o.publisher == publisher
32
33
  end
33
34
  end
34
35
 
36
+ def publishes?(object_name)
37
+ @publish.object?(object_name.to_s)
38
+ end
39
+
40
+ def consumes?(object_name)
41
+ @consume.object?(object_name.to_s)
42
+ end
43
+
44
+ def consumes_from?(service_name, object_name)
45
+ @consume.object?([service_name, object_name].join(Lacerda::SCOPE_SEPARATOR))
46
+ end
47
+
35
48
  def published_objects
36
49
  @publish.objects
37
50
  end
@@ -77,6 +90,10 @@ module Lacerda
77
90
  object_description.validate_data!(data)
78
91
  end
79
92
 
93
+ def consume_object_from(service_name, type, data)
94
+ consume_object([service_name, type].join(Lacerda::SCOPE_SEPARATOR), data)
95
+ end
96
+
80
97
  def consume_object(type, data)
81
98
  object_description = @consume.object(type)
82
99
  Blumquist.new(schema: object_description.schema, data: data)
@@ -1,3 +1,3 @@
1
1
  module Lacerda
2
- VERSION = '0.11.0'
2
+ VERSION = '0.12.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lacerda
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jannis Hermanns
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-04 00:00:00.000000000 Z
11
+ date: 2015-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.2'
89
+ version: '0.3'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.2'
96
+ version: '0.3'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: bundler
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +192,48 @@ dependencies:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: pry
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: pry-rescue
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: pry-stack_explorer
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
195
237
  description: Specify which objects your services publish or consume in MSON (markdown)
196
238
  and let this gem validate these contracts.
197
239
  email: