jimmy 0.1.0 → 0.1.1

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: 8467dcce9891fc3ea4098a9e89ec2591c58cf1df
4
- data.tar.gz: 76ce29801be14c4d29e640d1820e5ea23eda0bb5
3
+ metadata.gz: cc78f64835960e1b8d8724bee79adc324f710bb7
4
+ data.tar.gz: 647dab7d9050d3ec4642b55a2cbc2211bfac1ab9
5
5
  SHA512:
6
- metadata.gz: bd47a8d315ea3d3cea8f6faf37441ebb684f0fccb04d8819887bf727dfdefe7d89b810a3d3c09a37d4e2cb0cad50677476220368908da17ec690c29ac00e5d27
7
- data.tar.gz: 3b66e4dde7fafab70cb5d52941a4e1d0342509634da90fc9a3f736bbe4b17e5b8d7d2678120c115f9a58dc8a68e178be91c8b382f7188f59760086fb62cd8ae6
6
+ metadata.gz: d8f71fc6b11b45bca65a0f599ef0cfa161d43d0c93e208fa47ec42b840c9584a76a7f358b4063e3901bcc51e65c3b8db948e86d3b336363ec87be098db6cdc26
7
+ data.tar.gz: c11234863a1c7f3ccc8180c3e4f55b9d88844c648f34efa06aef0d30a31869aae7c21a345a1fcd0df7a0c3463cd71880f09863ce18cc4460dc35c0e0caafa46c
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
  spec.license = 'Apache License, Version 2.0'
21
21
 
22
+ spec.add_dependency 'json-schema', '~> 2.5'
23
+
22
24
  spec.add_development_dependency 'bundler', '~> 1.9'
23
25
  spec.add_development_dependency 'rake', '~> 10.0'
24
26
  spec.add_development_dependency 'rspec', '~> 3.2'
@@ -13,3 +13,4 @@ require_relative 'jimmy/schema_creation'
13
13
  require_relative 'jimmy/schema_types'
14
14
  require_relative 'jimmy/schema_type'
15
15
  require_relative 'jimmy/combination'
16
+ require_relative 'jimmy/validation_error'
@@ -15,8 +15,8 @@ module Jimmy
15
15
  instance_exec &types_proc
16
16
  end
17
17
 
18
- def serialize
19
- {"#{condition}Of" => map(&:serialize)}
18
+ def compile
19
+ {"#{condition}Of" => map(&:compile)}
20
20
  end
21
21
 
22
22
  SchemaCreation.apply_to(self) { |schema| push schema }
@@ -1,47 +1,94 @@
1
1
  require 'uri'
2
2
  require 'pathname'
3
+ require 'json'
4
+ require 'json-schema'
3
5
 
4
6
  require_relative 'schema_creation'
5
7
 
6
8
  module Jimmy
7
9
  class Domain
8
10
 
9
- attr_reader :root, :types
11
+ attr_reader :root, :types, :partials
10
12
 
11
13
  def initialize(root)
12
- @root = URI(root)
13
- @schemas = {}
14
- @types = {}
14
+ @root = URI(root)
15
+ @schemas = {}
16
+ @types = {}
17
+ @partials = {}
18
+ @import_paths = []
15
19
  end
16
20
 
17
21
  def domain
18
22
  self
19
23
  end
20
24
 
21
- def import_path(path)
25
+ def import(path)
22
26
  path = Pathname(path) unless path.is_a? Pathname
23
- @types = import_schemas(path + 'types', path).map { |k, v| [k.to_sym, v] }.to_h
24
- @schemas = import_schemas(path, path, 'types/')
27
+ @import_paths << path unless @import_paths.include? path
28
+
29
+ glob path, only: 'types' do |name, schema|
30
+ @types[name.to_sym] = schema
31
+ end
32
+
33
+ glob path, only: 'partials' do |name|
34
+ partial_path = path + 'partials' + "#{name}.rb"
35
+ @partials[name] = [partial_path.read, partial_path.to_s]
36
+ end
37
+
38
+ glob path, ignore: %r`^(types|partials)/` do |name, schema|
39
+ @schemas[name] = schema
40
+ end
41
+ end
42
+
43
+ def autoload_type(name)
44
+ # TODO: protect from circular dependency
45
+ return if types.key? name
46
+ @import_paths.each do |import_path|
47
+ path = import_path + "types/#{name}.rb"
48
+ if path.file?
49
+ @types[name] = load_schema_from_path(path, name)
50
+ return true
51
+ end
52
+ end
53
+ false
25
54
  end
26
55
 
27
56
  def [](schema_name)
28
57
  @schemas[schema_name.to_s]
29
58
  end
30
59
 
60
+ def export(path = nil)
61
+ path = Pathname(path) if path.is_a? String
62
+ raise 'Please specify an export directory' unless path.is_a?(Pathname) && (path.directory? || !path.exist?)
63
+ path.mkpath
64
+ @schemas.each { |name, schema| export_schema schema, path + "#{name.to_s}.json" }
65
+ @types.each { |name, schema| export_schema schema, path + 'types' + "#{name.to_s}.json" }
66
+ end
67
+
31
68
  private
32
69
 
33
- def import_schemas(path, base_path, reject_prefix = nil)
34
- result = {}
35
- Dir[path + '**/*.rb'].each do |full_path|
70
+ def glob(base_path, only: '.', ignore: nil, &block)
71
+ lookup_path = base_path + only
72
+ Dir[lookup_path + '**/*.rb'].each do |full_path|
36
73
  full_path = Pathname(full_path)
37
- relative_path = full_path.relative_path_from(path)
38
- next if reject_prefix && relative_path.to_s.start_with?(reject_prefix)
39
- base_name = relative_path.to_s[0..-4]
40
- schema = instance_eval(full_path.read, full_path.to_s).schema
41
- schema.name = full_path.relative_path_from(base_path).to_s[0..-4]
42
- result[base_name] = schema
74
+ relative_path = full_path.relative_path_from(lookup_path)
75
+ next if ignore === relative_path.to_s
76
+ args = [relative_path.to_s[0..-4]]
77
+ args << load_schema_from_path(full_path, full_path.relative_path_from(base_path).to_s[0..-4]) if block.arity == 2
78
+ yield *args
79
+ end
80
+ end
81
+
82
+ def load_schema_from_path(path, name)
83
+ instance_eval(path.read, path.to_s).schema.tap do |schema|
84
+ schema.name = name.to_s
85
+ JSON::Validator.add_schema JSON::Schema.new(schema.to_h, nil)
43
86
  end
44
- result
87
+ end
88
+
89
+ def export_schema(schema, target_path)
90
+ target_path.parent.mkpath
91
+ target_path.write JSON.pretty_generate(schema.to_h)
45
92
  end
46
93
 
47
94
  SchemaCreation.apply_to self
@@ -5,13 +5,7 @@ module Jimmy
5
5
  attr_accessor :name
6
6
 
7
7
  @argument_handlers = Hash.new { |hash, key| hash[key] = {} }
8
-
9
- def self.create(*args, &block)
10
- new(*args).tap do |schema|
11
- schema.dsl.evaluate block if block
12
- end
13
- end
14
-
8
+
15
9
  def self.set_argument_handler(schema_class, arg_class, handler)
16
10
  @argument_handlers[schema_class][arg_class] = handler
17
11
  end
@@ -26,28 +20,37 @@ module Jimmy
26
20
  result && result.last
27
21
  end
28
22
 
29
- def serialize
30
- serializer = nil
23
+ def compile
24
+ compiler = nil
31
25
  schema_class = SchemaTypes[type]
32
26
  until schema_class == SchemaType do
33
- serializer ||= SchemaTypes.serializers[schema_class]
27
+ compiler ||= SchemaTypes.compilers[schema_class]
34
28
  schema_class = schema_class.superclass
35
29
  end
36
30
  {'type' => type.to_s}.tap do |hash|
37
- dsl.evaluate serializer, hash if serializer
31
+ dsl.evaluate compiler, hash if compiler
38
32
  end
39
33
  end
40
34
 
35
+ def url
36
+ "#{domain.root}/#{name}.json#"
37
+ end
38
+
41
39
  def to_h
42
- {}.tap do |hash|
43
- hash['$schema'] = "#{domain.root}/#{name}#" if name
44
- hash.merge! serialize
40
+ {'$schema' => 'http://json-schema.org/draft-04/schema#'}.tap do |hash|
41
+ hash['id'] = url if name
42
+ hash.merge! compile
45
43
  end
46
44
  end
47
45
 
46
+ def validate(data)
47
+ errors = JSON::Validator.fully_validate(JSON::Validator.schema_for_uri(url).schema, data, errors_as_objects: true)
48
+ raise ValidationError.new(self, data, errors) unless errors.empty?
49
+ end
50
+
48
51
  private
49
52
 
50
- def initialize(type, domain, *args)
53
+ def initialize(type, domain, *args, &block)
51
54
  @attrs = {}
52
55
  @type = type
53
56
  @domain = domain
@@ -64,6 +67,7 @@ module Jimmy
64
67
  dsl.evaluate handler, arg
65
68
  end
66
69
  end
70
+ dsl.evaluate block if block
67
71
  end
68
72
 
69
73
  end
@@ -32,13 +32,15 @@ module Jimmy
32
32
  handler = SchemaCreation.handlers[self.class]
33
33
  self.class.__send__ :define_method, method do |*inner_args, &inner_block|
34
34
  handler_args = handler && inner_args.shift(handler.arity - 1)
35
- schema = Schema.create(method, domain, *inner_args, &inner_block)
35
+ schema = Schema.new(method, domain, *inner_args, &inner_block)
36
36
  instance_exec schema, *handler_args, &handler if handler
37
37
  schema.dsl
38
38
  end
39
39
  return __send__ method, *args, &block
40
40
  end
41
41
 
42
+ domain.autoload_type method
43
+
42
44
  if domain.types.key? method
43
45
  return instance_exec method, *args, &SchemaCreation.handlers[self.class]
44
46
  end
@@ -27,8 +27,8 @@ module Jimmy
27
27
  SchemaTypes.nested_handlers[self] = handler
28
28
  end
29
29
 
30
- def serialize(&handler)
31
- SchemaTypes.serializers[self] = handler
30
+ def compile(&handler)
31
+ SchemaTypes.compilers[self] = handler
32
32
  end
33
33
 
34
34
  end
@@ -53,8 +53,12 @@ module Jimmy
53
53
  included_args.map { |arg| [arg.to_s.gsub(/_([a-z])/) { $1.upcase }, attrs[arg]] }.to_h
54
54
  end
55
55
 
56
- def serialize_schema(schema)
57
- schema.is_a?(Symbol) ? {'$ref' => "/types/#{schema}#"} : schema.serialize
56
+ def compile_schema(schema)
57
+ schema.is_a?(Symbol) ? {'$ref' => "/types/#{schema}.json#"} : schema.compile
58
+ end
59
+
60
+ def include(*partial_names)
61
+ partial_names.each { |name| instance_eval *domain.partials[name.to_s] }
58
62
  end
59
63
 
60
64
  end
@@ -9,14 +9,14 @@ module Jimmy
9
9
  @types = {}
10
10
  @dsls = {}
11
11
  @nested_handlers = {}
12
- @serializers = {}
12
+ @compilers = {}
13
13
 
14
14
  class << self
15
15
  extend Forwardable
16
16
 
17
17
  delegate %i[each keys values key?] => :@types
18
18
 
19
- attr_reader :dsls, :nested_handlers, :serializers
19
+ attr_reader :dsls, :nested_handlers, :compilers
20
20
 
21
21
  def [](type_name)
22
22
  @types[type_name]
@@ -15,13 +15,13 @@ module Jimmy
15
15
  (attrs[:items] ||= []) << schema
16
16
  end
17
17
 
18
- serialize do |hash|
18
+ compile do |hash|
19
19
  hash.merge! camelize_attrs(%i[min_items max_items])
20
20
  items = attrs[:items] || []
21
21
  if items.length > 1
22
- hash['items'] = {'anyOf' => items.map { |i| serialize_schema i }}
22
+ hash['items'] = {'anyOf' => items.map { |i| compile_schema i }}
23
23
  elsif items.length == 1
24
- hash['items'] = serialize_schema(items.first)
24
+ hash['items'] = compile_schema(items.first)
25
25
  end
26
26
  end
27
27
 
@@ -22,7 +22,7 @@ module Jimmy
22
22
  end
23
23
  end
24
24
 
25
- serialize do |hash|
25
+ compile do |hash|
26
26
  hash.merge! camelize_attrs(%i[minimum maximum exclusive_minimum exclusive_maximum multiple_of])
27
27
  end
28
28
 
@@ -23,7 +23,7 @@ module Jimmy
23
23
  (attrs[:properties] ||= {})[property_name] = schema
24
24
  end
25
25
 
26
- serialize do |hash|
26
+ compile do |hash|
27
27
  (attrs[:properties] || {}).each do |key, value|
28
28
  collection, key =
29
29
  if key.is_a? Regexp
@@ -32,7 +32,7 @@ module Jimmy
32
32
  ['properties', key.to_s]
33
33
  end
34
34
  hash[collection] ||= {}
35
- hash[collection][key] = serialize_schema(value)
35
+ hash[collection][key] = compile_schema(value)
36
36
  end
37
37
  hash['required'] = (attrs[:required] || []).to_a
38
38
  hash['additionalProperties'] = !!attrs[:additional_properties]
@@ -6,11 +6,20 @@ module Jimmy
6
6
  trait :max_length
7
7
  trait(:pattern) { |regex| attrs[:pattern] = regex.is_a?(Regexp) ? regex.inspect.gsub(%r`^/|/[a-z]*$`, '') : regex }
8
8
  trait(Regexp) { |regex| pattern regex }
9
- trait(Range) { |value| attrs[:min_length], attrs[:max_length] = [value.first, value.last].sort }
9
+ trait Range do |value|
10
+ variation = value.exclude_end? ? 1 : 0
11
+ if value.first < value.last
12
+ attrs[:min_length] = value.first
13
+ attrs[:max_length] = value.last - variation
14
+ else
15
+ attrs[:max_length] = value.first
16
+ attrs[:min_length] = value.last + variation
17
+ end
18
+ end
10
19
  trait(Fixnum) { |value| min_length value; max_length value }
11
20
  trait(Array) { |value| attrs[:enum] = value.map(&:to_s) }
12
21
 
13
- serialize do |hash|
22
+ compile do |hash|
14
23
  hash.merge! camelize_attrs(%i[min_length max_length pattern enum])
15
24
  end
16
25
 
@@ -0,0 +1,20 @@
1
+ require 'ostruct'
2
+
3
+ module Jimmy
4
+ class ValidationError < StandardError
5
+
6
+ attr_reader :schema, :data, :errors
7
+
8
+ def initialize(schema, data, errors)
9
+ @schema = schema
10
+ @data = data
11
+ @errors = errors.map do |info|
12
+ OpenStruct.new(
13
+ property: info[:fragment][2..-1],
14
+ message: info[:message].gsub(/ in schema \S+$/, ''),
15
+ aspect: info[:failed_attribute]
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module Jimmy
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jimmy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil E. Pearson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-29 00:00:00.000000000 Z
11
+ date: 2015-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json-schema
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.5'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +95,7 @@ files:
81
95
  - lib/jimmy/schema_types/object.rb
82
96
  - lib/jimmy/schema_types/string.rb
83
97
  - lib/jimmy/symbol_array.rb
98
+ - lib/jimmy/validation_error.rb
84
99
  - lib/jimmy/version.rb
85
100
  homepage: https://github.com/orionvm/jimmy
86
101
  licenses: