jimmy 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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