jimmy 0.5.5

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 189b5833da044ec29e5cf06d708960564a08a47f
4
+ data.tar.gz: 30c672d61e39c2e84e131404b6efdf5c4bdcecad
5
+ SHA512:
6
+ metadata.gz: 1e225b091d344c3c72fd660e3672821c49c2be5403a8b43db54f2f22b2b0cf3fe2ce4867c3371d6462fed1ca7395c274fed29ef7f634ebb012ba9e3ebe161204
7
+ data.tar.gz: 3ee1e87f5329b4296ceaa9b664f8aa13c10ffcf7f72ffdbd1b486829e77b603a8a2e467552057e0801ccd458d566c375b914e82de62d480503fdb56c9ed3fca2
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .rspec
11
+ *.gem
@@ -0,0 +1 @@
1
+ 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2015 OrionVM Pty Ltd
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,151 @@
1
+ # Jimmy the Gem
2
+
3
+ Meet your mate Jimmy. He's a top bloke.
4
+
5
+ Writing JSON schemas is as tedious as a tax audit. But now, thanks to everyone's new best friend Jimmy, it's easier than shooting the side of a barn with a very easy-to-use projectile weapon.
6
+
7
+ ## Getting hooked up
8
+
9
+ You guessed it:
10
+
11
+ ```bash
12
+ $ gem install jimmy
13
+ ```
14
+
15
+ Here's another doozy:
16
+
17
+ ```ruby
18
+ require 'jimmy'
19
+ ```
20
+
21
+ Wasn't that a shock. Let's move on.
22
+
23
+ ## The DSL
24
+
25
+ Jimmy replaces `.json` JSON schemas files with `.rb` files. Here's a simple example:
26
+
27
+ ```ruby
28
+ # city.rb
29
+ object do
30
+ string :name, min_length: 2
31
+ string :postcode, /^\d{4}$/
32
+
33
+ require all
34
+ end
35
+ ```
36
+
37
+ This compiles to:
38
+
39
+ ```json
40
+ {
41
+ "$schema": "https://my.domain.kom/city#",
42
+ "type": "object",
43
+ "properties": {
44
+ "name": {
45
+ "type": "string",
46
+ "minLength": 2
47
+ },
48
+ "postcode": {
49
+ "type": "string",
50
+ "pattern": "^\\d{4}$"
51
+ },
52
+ "required": ["name", "zipcode"],
53
+ "additionalProperties": false
54
+ }
55
+ }
56
+ ```
57
+
58
+ Crikey! That's a bit of a difference. Let's take a look at a more complex (and contrived) example:
59
+
60
+ ```ruby
61
+ # types/country_code.rb
62
+ string /^[A-Z]{2}$/
63
+
64
+ # types/geopoint.rb
65
+ object do
66
+ number :latitude, -90..90
67
+ number :longitude, -180..180
68
+ end
69
+
70
+ # city.rb
71
+ object do
72
+ string :name, min_length: 2
73
+ string :postcode, /^\d{4}$/
74
+ integer :population
75
+ geopoint :location
76
+ country_code :country
77
+ array :points_of_interest do
78
+ object do
79
+ string :title, 3..150
80
+ integer :popularity, [1, 3, 5, 7]
81
+ geopoint :location
82
+ boolean :featured
83
+ require :title
84
+ end
85
+ end
86
+ require all - :location
87
+ end
88
+ ```
89
+
90
+ Here's the full result (though we expect you get the gist of it by now):
91
+
92
+ ```json
93
+ {
94
+ "$schema": "https://my.domain.kom/city#",
95
+ "type": "object",
96
+ "properties": {
97
+ "name": {
98
+ "type": "string",
99
+ "minLength": 2
100
+ },
101
+ "postcode": {
102
+ "type": "string",
103
+ "pattern": "^\\d{4}$"
104
+ },
105
+ "population": {
106
+ "type": "integer"
107
+ },
108
+ "location": {
109
+ "$ref": "/types/geopoint#"
110
+ },
111
+ "country": {
112
+ "$ref": "/types/country_code#"
113
+ },
114
+ "points_of_interest": {
115
+ "type": "array",
116
+ "items": {
117
+ "type": "object",
118
+ "properties": {
119
+ "title": {
120
+ "type": "string",
121
+ "minLength": 3,
122
+ "maxLength": 150
123
+ },
124
+ "popularity": {
125
+ "type": "integer",
126
+ "enum": [1, 3, 5, 7]
127
+ },
128
+ "location": {
129
+ "$ref": "/types/geopoint#"
130
+ },
131
+ "featured": {
132
+ "type": "boolean"
133
+ }
134
+ },
135
+ "required": [
136
+ "title"
137
+ ],
138
+ "additionalProperties": false
139
+ }
140
+ }
141
+ },
142
+ "required": [
143
+ "name",
144
+ "postcode",
145
+ "population",
146
+ "country",
147
+ "points_of_interest"
148
+ ],
149
+ "additionalProperties": false
150
+ }
151
+ ```
@@ -0,0 +1,4 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new :spec
@@ -0,0 +1,11 @@
1
+ machine:
2
+ ruby:
3
+ version: '2.1.5'
4
+
5
+ dependencies:
6
+ pre:
7
+ - gem install bundler -v '~>1.9'
8
+
9
+ test:
10
+ override:
11
+ - bundle exec rake spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jimmy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'jimmy'
8
+ spec.version = Jimmy::VERSION
9
+ spec.authors = ['Neil E. Pearson']
10
+ spec.email = ['neil.pearson@orionvm.com']
11
+
12
+ spec.summary = 'Jimmy the JSON Schema DSL'
13
+ spec.description = 'Jimmy makes it a snap to compose detailed JSON schema documents.'
14
+ spec.homepage = 'https://github.com/hx/jimmy'
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 = 'Apache License, Version 2.0'
21
+
22
+ spec.add_dependency 'json-schema', '~> 2.5'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.9'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.2'
27
+ spec.add_development_dependency 'diff_matcher', '~> 2.7'
28
+ end
@@ -0,0 +1,20 @@
1
+ require 'pathname'
2
+ require 'jimmy/version'
3
+
4
+ module Jimmy
5
+ ROOT = Pathname(__FILE__).parent.parent
6
+ end
7
+
8
+ require_relative 'jimmy/symbol_array'
9
+
10
+ require_relative 'jimmy/domain'
11
+ require_relative 'jimmy/schema'
12
+ require_relative 'jimmy/reference'
13
+ require_relative 'jimmy/type_reference'
14
+ require_relative 'jimmy/schema_creation'
15
+ require_relative 'jimmy/schema_types'
16
+ require_relative 'jimmy/schema_type'
17
+ require_relative 'jimmy/combination'
18
+ require_relative 'jimmy/link'
19
+ require_relative 'jimmy/validation_error'
20
+ require_relative 'jimmy/definitions'
@@ -0,0 +1,34 @@
1
+ require_relative 'schema_types'
2
+
3
+ module Jimmy
4
+ class Combination < Array
5
+ include SchemaCreation::Referencing
6
+
7
+ attr_reader :condition, :schema
8
+
9
+ # @param [Symbol] condition One of :one, :all, or :any
10
+ def initialize(condition, schema)
11
+ @condition = condition
12
+ @schema = schema
13
+ end
14
+
15
+ def domain
16
+ schema.domain
17
+ end
18
+
19
+ def evaluate(types_proc)
20
+ instance_exec &types_proc
21
+ end
22
+
23
+ def compile
24
+ data.merge "#{condition}Of" => map(&:compile)
25
+ end
26
+
27
+ def data
28
+ @data ||= {}
29
+ end
30
+
31
+ SchemaCreation.apply_to(self) { |schema| push schema }
32
+
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ require 'forwardable'
2
+
3
+ module Jimmy
4
+ class Definitions
5
+ include SchemaCreation::Referencing
6
+ extend Forwardable
7
+ delegate %i[empty? key? map] => :@values
8
+
9
+ attr_reader :schema
10
+
11
+ def initialize(schema)
12
+ @schema = schema
13
+ @values = {}
14
+ end
15
+
16
+ def evaluate(&block)
17
+ instance_exec &block
18
+ end
19
+
20
+ def domain
21
+ schema.domain
22
+ end
23
+
24
+ def compile
25
+ map { |k, v| [k.to_s, v.compile] }.to_h
26
+ end
27
+
28
+ def data
29
+ schema.data
30
+ end
31
+
32
+ def [](key)
33
+ @values[key] || (schema.parent && schema.parent.definitions[key])
34
+ end
35
+
36
+ SchemaCreation.apply_to(self) { |schema, name| @values[name] = schema }
37
+ end
38
+ end
@@ -0,0 +1,111 @@
1
+ require 'uri'
2
+ require 'pathname'
3
+ require 'json'
4
+ require 'json-schema'
5
+
6
+ require_relative 'schema_creation'
7
+
8
+ module Jimmy
9
+ class Domain
10
+ DEFAULT_OPTIONS = {
11
+ transform_keys: nil
12
+ }
13
+
14
+ attr_reader :root, :types, :partials, :options, :schemas
15
+
16
+ def initialize(root, **options)
17
+ @root = URI(root)
18
+ @schemas = {}
19
+ @types = {}
20
+ @partials = {}
21
+ @import_paths = []
22
+ @uri_formatter = -> _, name { @root + "#{name}.json#" }
23
+ @options = DEFAULT_OPTIONS.merge(options)
24
+ end
25
+
26
+ def domain
27
+ self
28
+ end
29
+
30
+ def import(path)
31
+ path = Pathname(path) unless path.is_a? Pathname
32
+ @import_paths << path unless @import_paths.include? path
33
+
34
+ glob path, only: 'types' do |name, schema|
35
+ @types[name.to_sym] = schema
36
+ end
37
+
38
+ glob path, only: 'partials' do |name|
39
+ partial_path = path + 'partials' + "#{name}.rb"
40
+ @partials[name] = [partial_path.read, partial_path.to_s]
41
+ end
42
+
43
+ glob path, ignore: %r`^(types|partials)/` do |name, schema|
44
+ @schemas[name] = schema
45
+ end
46
+ end
47
+
48
+ def autoload_type(name)
49
+ # TODO: protect from circular dependency
50
+ return if types.key? name
51
+ @import_paths.each do |import_path|
52
+ path = import_path + "types/#{name}.rb"
53
+ if path.file?
54
+ @types[name] = load_schema_from_path(path, name)
55
+ return true
56
+ end
57
+ end
58
+ false
59
+ end
60
+
61
+ def [](schema_name)
62
+ @schemas[schema_name.to_s]
63
+ end
64
+
65
+ def export(path = nil, &serializer)
66
+ path = Pathname(path) if path.is_a? String
67
+ raise 'Please specify an export directory' unless path.is_a?(Pathname) && (path.directory? || !path.exist?)
68
+ path.mkpath
69
+ @schemas.each { |name, schema| export_schema schema, path + "#{name.to_s}.json", &serializer }
70
+ @types.each { |name, schema| export_schema schema, path + 'types' + "#{name.to_s}.json", &serializer }
71
+ end
72
+
73
+ def uri_for(name)
74
+ @uri_formatter.call root, name
75
+ end
76
+
77
+ def uri_format(&block)
78
+ @uri_formatter = block
79
+ end
80
+
81
+ private
82
+
83
+ def glob(base_path, only: '.', ignore: nil, &block)
84
+ lookup_path = base_path + only
85
+ Dir[lookup_path + '**/*.rb'].each do |full_path|
86
+ full_path = Pathname(full_path)
87
+ relative_path = full_path.relative_path_from(lookup_path)
88
+ next if ignore === relative_path.to_s
89
+ args = [relative_path.to_s[0..-4]]
90
+ args << load_schema_from_path(full_path, full_path.relative_path_from(base_path).to_s[0..-4]) if block.arity == 2
91
+ yield *args
92
+ end
93
+ end
94
+
95
+ def load_schema_from_path(path, name)
96
+ @schema_name = name
97
+ instance_eval(path.read, path.to_s).schema.tap do |schema|
98
+ schema.name = name.to_s
99
+ JSON::Validator.add_schema JSON::Schema.new(schema.to_h, nil)
100
+ end
101
+ end
102
+
103
+ def export_schema(schema, target_path)
104
+ target_path.parent.mkpath
105
+ target_path.write block_given? ? yield(schema.to_h) : JSON.pretty_generate(schema.to_h)
106
+ end
107
+
108
+ SchemaCreation.apply_to self
109
+
110
+ end
111
+ end