jimmy 0.1.0 → 0.1.1

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: 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: