lacerda 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: