json-schema_builder 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +40 -5
  3. data/Appraisals +6 -3
  4. data/gemfiles/{activesupport_4.0.gemfile → version_2.1.0_4.0.gemfile} +1 -0
  5. data/gemfiles/{activesupport_4.1.gemfile → version_2.1.0_4.1.gemfile} +1 -0
  6. data/gemfiles/{activesupport_4.2.gemfile → version_2.1.0_4.2.gemfile} +1 -0
  7. data/gemfiles/{activesupport_5.0.gemfile → version_2.1.0_5.0.gemfile} +1 -0
  8. data/gemfiles/{activesupport_5.1.gemfile → version_2.1.0_5.1.gemfile} +1 -0
  9. data/gemfiles/version_2.2.0_4.0.gemfile +8 -0
  10. data/gemfiles/version_2.2.0_4.1.gemfile +8 -0
  11. data/gemfiles/version_2.2.0_4.2.gemfile +8 -0
  12. data/gemfiles/version_2.2.0_5.0.gemfile +8 -0
  13. data/gemfiles/version_2.2.0_5.1.gemfile +8 -0
  14. data/gemfiles/version_2.3.0_4.0.gemfile +8 -0
  15. data/gemfiles/version_2.3.0_4.1.gemfile +8 -0
  16. data/gemfiles/version_2.3.0_4.2.gemfile +8 -0
  17. data/gemfiles/version_2.3.0_5.0.gemfile +8 -0
  18. data/gemfiles/version_2.3.0_5.1.gemfile +8 -0
  19. data/gemfiles/version_2.4.0_4.0.gemfile +8 -0
  20. data/gemfiles/version_2.4.0_4.1.gemfile +8 -0
  21. data/gemfiles/version_2.4.0_4.2.gemfile +8 -0
  22. data/gemfiles/version_2.4.0_5.0.gemfile +8 -0
  23. data/gemfiles/version_2.4.0_5.1.gemfile +8 -0
  24. data/gemfiles/version_2.5.0_4.0.gemfile +8 -0
  25. data/gemfiles/version_2.5.0_4.1.gemfile +8 -0
  26. data/gemfiles/version_2.5.0_4.2.gemfile +8 -0
  27. data/gemfiles/version_2.5.0_5.0.gemfile +8 -0
  28. data/gemfiles/version_2.5.0_5.1.gemfile +8 -0
  29. data/gemfiles/version_2.6.0_4.0.gemfile +8 -0
  30. data/gemfiles/version_2.6.0_4.1.gemfile +8 -0
  31. data/gemfiles/version_2.6.0_4.2.gemfile +8 -0
  32. data/gemfiles/version_2.6.0_5.0.gemfile +8 -0
  33. data/gemfiles/version_2.6.0_5.1.gemfile +8 -0
  34. data/gemfiles/version_2.7.0_4.0.gemfile +8 -0
  35. data/gemfiles/version_2.7.0_4.1.gemfile +8 -0
  36. data/gemfiles/version_2.7.0_4.2.gemfile +8 -0
  37. data/gemfiles/version_2.7.0_5.0.gemfile +8 -0
  38. data/gemfiles/version_2.7.0_5.1.gemfile +8 -0
  39. data/gemfiles/version_2.8.0_4.0.gemfile +8 -0
  40. data/gemfiles/version_2.8.0_4.1.gemfile +8 -0
  41. data/gemfiles/version_2.8.0_4.2.gemfile +8 -0
  42. data/gemfiles/version_2.8.0_5.0.gemfile +8 -0
  43. data/gemfiles/version_2.8.0_5.1.gemfile +8 -0
  44. data/json-schema_builder.gemspec +1 -1
  45. data/lib/json/schema_builder/array.rb +1 -0
  46. data/lib/json/schema_builder/entity.rb +22 -5
  47. data/lib/json/schema_builder/schema.rb +23 -3
  48. data/lib/json/schema_builder/validation.rb +27 -1
  49. data/lib/json/schema_builder/version.rb +1 -1
  50. data/lib/json/schema_builder.rb +1 -1
  51. data/spec/integration/custom_errors_spec.rb +36 -0
  52. data/spec/support/examples/custom_errors.rb +48 -0
  53. data/spec/support/shared_contexts_for_entity.rb +1 -1
  54. data/spec/unit/entity_spec.rb +1 -1
  55. data/spec/unit/schema_spec.rb +228 -48
  56. metadata +48 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '0912320b00a0b8b43307a99560e5ac3f5f6eadee'
4
- data.tar.gz: d74be2254534647d26b11c3f84dc22b67e9ac477
3
+ metadata.gz: 7e1e931da1f8e1ef88c5f267408422b8195f7d85
4
+ data.tar.gz: 561d28974eb9bcaa95d84c3799db95ea466ee3b1
5
5
  SHA512:
6
- metadata.gz: 0d9d7ea22df0b6c54e82cbb18edbe29e28e9f43efe51d97e306fec2eb68599c44ca9adf8c68ebfeb78a48010f3037f53566be7dfc7e1b024c0af6b2843fdac2d
7
- data.tar.gz: f65201e295b2b744af78907cc67f57d85f1e4d9280ede6b2c8d945443da56fb2ead8237f78f10aa321e5246ef3db5fddd6b4447709a42830f8e45849e056648e
6
+ metadata.gz: de361202331e17c8b447fda6f5d76de261edef07f367af44493ef4bd1034c040e97a4efbd7c865ed18bd33ef10316bd42543d74d34be1ff6c6b907bf4aa018f7
7
+ data.tar.gz: 0d23b47272718d8ca60b575ed39b10c562af101d7537e2b27e064ef8ea4ef87cb6b5822a9eedc65cc1deb8a93f24b555373048a8c68bf4a05671d97f2fab0814
data/.travis.yml CHANGED
@@ -9,8 +9,43 @@ addons:
9
9
  repo_token:
10
10
  secure: X3mDel/6C4d8uSeaKgxA2YP4Oen5N2iklmi2mH1XzK3UihzlNKcQLxYC6IMTTfYiPlLu1BTO1KTElUqWIigpHxAAJj/Lb48S8mvKFAsmv48tOP5LaTeFNdnJA49k0hCXQbItlyVBmUhEtkdyelLciP3ygcKgEcR/jZGcJM/aZBY=
11
11
  gemfile:
12
- - gemfiles/activesupport_4.0.gemfile
13
- - gemfiles/activesupport_4.1.gemfile
14
- - gemfiles/activesupport_4.2.gemfile
15
- - gemfiles/activesupport_5.0.gemfile
16
- - gemfiles/activesupport_5.1.gemfile
12
+ - gemfiles/version_2.1.0_4.0.gemfile
13
+ - gemfiles/version_2.1.0_4.1.gemfile
14
+ - gemfiles/version_2.1.0_4.2.gemfile
15
+ - gemfiles/version_2.1.0_5.0.gemfile
16
+ - gemfiles/version_2.1.0_5.1.gemfile
17
+ - gemfiles/version_2.2.0_4.0.gemfile
18
+ - gemfiles/version_2.2.0_4.1.gemfile
19
+ - gemfiles/version_2.2.0_4.2.gemfile
20
+ - gemfiles/version_2.2.0_5.0.gemfile
21
+ - gemfiles/version_2.2.0_5.1.gemfile
22
+ - gemfiles/version_2.3.0_4.0.gemfile
23
+ - gemfiles/version_2.3.0_4.1.gemfile
24
+ - gemfiles/version_2.3.0_4.2.gemfile
25
+ - gemfiles/version_2.3.0_5.0.gemfile
26
+ - gemfiles/version_2.3.0_5.1.gemfile
27
+ - gemfiles/version_2.4.0_4.0.gemfile
28
+ - gemfiles/version_2.4.0_4.1.gemfile
29
+ - gemfiles/version_2.4.0_4.2.gemfile
30
+ - gemfiles/version_2.4.0_5.0.gemfile
31
+ - gemfiles/version_2.4.0_5.1.gemfile
32
+ - gemfiles/version_2.5.0_4.0.gemfile
33
+ - gemfiles/version_2.5.0_4.1.gemfile
34
+ - gemfiles/version_2.5.0_4.2.gemfile
35
+ - gemfiles/version_2.5.0_5.0.gemfile
36
+ - gemfiles/version_2.5.0_5.1.gemfile
37
+ - gemfiles/version_2.6.0_4.0.gemfile
38
+ - gemfiles/version_2.6.0_4.1.gemfile
39
+ - gemfiles/version_2.6.0_4.2.gemfile
40
+ - gemfiles/version_2.6.0_5.0.gemfile
41
+ - gemfiles/version_2.6.0_5.1.gemfile
42
+ - gemfiles/version_2.7.0_4.0.gemfile
43
+ - gemfiles/version_2.7.0_4.1.gemfile
44
+ - gemfiles/version_2.7.0_4.2.gemfile
45
+ - gemfiles/version_2.7.0_5.0.gemfile
46
+ - gemfiles/version_2.7.0_5.1.gemfile
47
+ - gemfiles/version_2.8.0_4.0.gemfile
48
+ - gemfiles/version_2.8.0_4.1.gemfile
49
+ - gemfiles/version_2.8.0_4.2.gemfile
50
+ - gemfiles/version_2.8.0_5.0.gemfile
51
+ - gemfiles/version_2.8.0_5.1.gemfile
data/Appraisals CHANGED
@@ -1,5 +1,8 @@
1
- %w(4.0 4.1 4.2 5.0 5.1).each do |version|
2
- appraise "activesupport-#{version}" do
3
- gem "activesupport", "~> #{version}"
1
+ %w(2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0).each do |json_schema_version|
2
+ %w(4.0 4.1 4.2 5.0 5.1).each do |active_support_version|
3
+ appraise "version-#{json_schema_version}-#{active_support_version}" do
4
+ gem "json-schema", "~> #{json_schema_version}"
5
+ gem "activesupport", "~> #{active_support_version}"
6
+ end
4
7
  end
5
8
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "json-schema", "~> 2.1.0"
5
6
  gem "activesupport", "~> 4.0"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "json-schema", "~> 2.1.0"
5
6
  gem "activesupport", "~> 4.1"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "json-schema", "~> 2.1.0"
5
6
  gem "activesupport", "~> 4.2"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "json-schema", "~> 2.1.0"
5
6
  gem "activesupport", "~> 5.0"
6
7
 
7
8
  gemspec path: "../"
@@ -2,6 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "json-schema", "~> 2.1.0"
5
6
  gem "activesupport", "~> 5.1"
6
7
 
7
8
  gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.2.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.2.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.2.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.2.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.2.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.3.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.3.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.3.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.3.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.3.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.4.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.4.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.4.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.4.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.4.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.5.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.5.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.5.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.5.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.5.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.6.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.6.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.6.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.6.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.6.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.7.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.7.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.7.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.7.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.7.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.8.0"
6
+ gem "activesupport", "~> 4.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.8.0"
6
+ gem "activesupport", "~> 4.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.8.0"
6
+ gem "activesupport", "~> 4.2"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.8.0"
6
+ gem "activesupport", "~> 5.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "json-schema", "~> 2.8.0"
6
+ gem "activesupport", "~> 5.1"
7
+
8
+ gemspec path: "../"
@@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'pry-byebug'
26
26
  spec.add_development_dependency 'appraisal'
27
27
  spec.add_dependency 'activesupport', '>= 4.0'
28
- spec.add_dependency 'json-schema', '>= 2'
28
+ spec.add_dependency 'json-schema', '>= 2.1'
29
29
  end
@@ -18,6 +18,7 @@ module JSON
18
18
  protected
19
19
 
20
20
  def items_entity(opts, &block)
21
+ opts[:parent] = self
21
22
  if opts[:type]
22
23
  send opts.delete(:type), opts, &block
23
24
  else
@@ -12,7 +12,7 @@ module JSON
12
12
  include Validation
13
13
  include Helpers
14
14
  class_attribute :registered_type
15
- attr_accessor :name, :parent, :children, :options
15
+ attr_accessor :name, :parent, :children, :options, :fragment, :fragments, :error
16
16
 
17
17
  attribute :title
18
18
  attribute :description
@@ -30,19 +30,30 @@ module JSON
30
30
  def initialize(name, opts = { }, &block)
31
31
  @name = name
32
32
  @children = []
33
+ @fragments = Hash.new { |hash, key| hash[key] = ::Array.new }
34
+ @fragments["#/"] << self if opts[:root]
33
35
  self.type = self.class.registered_type
34
36
  initialize_parent_with opts
35
37
  initialize_with opts
36
38
  eval_block &block
37
39
  end
38
40
 
41
+ def add_fragment(child)
42
+ @fragments[child.fragment] << child
43
+ @parent.add_fragment(child) if @parent
44
+ end
45
+
39
46
  def schema
40
- @schema ||= Schema.new
47
+ @schema ||= Schema.new({}, self)
48
+ end
49
+
50
+ def required
51
+ schema["required"] || []
41
52
  end
42
53
 
43
54
  def required=(*values)
44
- @parent.required ||= []
45
- @parent.required << @name
55
+ @parent.schema["required"] ||= []
56
+ @parent.schema["required"] << @name
46
57
  end
47
58
 
48
59
  def merge_children!
@@ -75,7 +86,13 @@ module JSON
75
86
 
76
87
  def initialize_parent_with(opts)
77
88
  @parent = opts.delete :parent
78
- @parent.children << self if @parent
89
+ if parent
90
+ @fragment = [@parent.fragment, name].compact.join("/").gsub(%r(//), "/")
91
+ parent.children << self
92
+ parent.add_fragment(self)
93
+ else
94
+ @fragment = "#/"
95
+ end
79
96
  end
80
97
 
81
98
  def initialize_with(opts)
@@ -1,19 +1,39 @@
1
+ require_relative "validation"
2
+
1
3
  module JSON
2
4
  module SchemaBuilder
3
5
  class Schema
4
- attr_accessor :data
6
+ include Validation
7
+
8
+ attr_accessor :data, :entities
5
9
  delegate :[], :[]=, :to_h, :as_json, to: :data
6
10
 
7
- def initialize(hash = {})
11
+ def initialize(hash = {}, entities = nil)
8
12
  @data = hash.with_indifferent_access
13
+ @entities = Array(entities)
14
+ end
15
+
16
+ def options
17
+ JSON::SchemaBuilder.options.to_h
18
+ end
19
+
20
+ def fragments
21
+ fragment_map = Hash.new { |hash, key| hash[key] = [] }
22
+ entities.map(&:fragments).each do |entity_fragments|
23
+ entity_fragments.each do |fragment, entity|
24
+ fragment_map[fragment] += entity
25
+ end
26
+ end
27
+ fragment_map
9
28
  end
10
29
 
11
30
  def merge(schema)
12
- self.class.new _deep_merge(data, schema.data)
31
+ self.class.new _deep_merge(data, schema.data), entities + schema.entities
13
32
  end
14
33
 
15
34
  def merge!(schema)
16
35
  @data = _deep_merge(data, schema.data)
36
+ @entities += schema.entities
17
37
  self
18
38
  end
19
39
 
@@ -19,7 +19,33 @@ module JSON
19
19
 
20
20
  def _validate(validator, data, opts)
21
21
  opts.reverse_merge! options if options
22
- JSON::Validator.send validator, as_json, data, opts
22
+ customize_errors = opts.delete(:customize_errors)
23
+ opts[:errors_as_objects] = true if customize_errors
24
+ validation_errors = JSON::Validator.send validator, as_json, data, opts
25
+ customize_errors ? _customize_errors(validation_errors) : validation_errors
26
+ end
27
+
28
+ def _customize_errors(error_objects)
29
+ _flatten_errors(error_objects).each do |error|
30
+ entities = Array(fragments[error[:fragment]]).select(&:error)
31
+ entities.each do |entity|
32
+ handler = entity.error
33
+ case handler
34
+ when ::Proc
35
+ handler.call(entities, error)
36
+ when ::String
37
+ error[:message] = entity.error
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def _flatten_errors(error_objects)
44
+ error_objects.map do |error_object|
45
+ sub_errors = error_object.delete(:errors) || []
46
+ sub_errors = sub_errors.values.flatten if sub_errors.is_a?(Hash)
47
+ [error_object, _flatten_errors(sub_errors)]
48
+ end.flatten
23
49
  end
24
50
  end
25
51
  end
@@ -1,5 +1,5 @@
1
1
  module JSON
2
2
  module SchemaBuilder
3
- VERSION = '0.4.0'
3
+ VERSION = '0.5.0'
4
4
  end
5
5
  end
@@ -12,10 +12,10 @@ module JSON
12
12
  module SchemaBuilder
13
13
  extend ActiveSupport::Concern
14
14
  include DSL
15
- include Helpers
16
15
  extend JSON::SchemaBuilder::Configuration
17
16
 
18
17
  included do |klass|
18
+ include Helpers
19
19
  extend JSON::SchemaBuilder::Configuration
20
20
  class << self
21
21
  attr_accessor :root_key
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Examples::CustomErrors, type: :integration do
4
+ let(:klass) { described_class.new({}) }
5
+
6
+ it "customizes with a proc" do
7
+ errors = schema.fully_validate({ user: "123", settings: { name: 456 } }, customize_errors: true)
8
+ expect(errors).to include hash_including(message: "Custom object error")
9
+ end
10
+
11
+ it "customizes with a string" do
12
+ errors = schema.fully_validate({ settings: { name: 456 } }, customize_errors: true)
13
+ expect(errors).to include hash_including(message: "Custom name error")
14
+ end
15
+
16
+ it "customizes required errors" do
17
+ errors = schema.fully_validate({ settings: { } }, customize_errors: true)
18
+ expect(errors).to include hash_including(message: %q(#/ requires properties ["user"]))
19
+ expect(errors).to include hash_including(message: %q(#/settings requires properties ["name"]))
20
+ end
21
+
22
+ it "merges error handlers" do
23
+ schema = klass.example.schema.merge(klass.example2.schema)
24
+ errors = schema.fully_validate({ settings: { other: 123 } }, customize_errors: true)
25
+ expect(errors).to include hash_including(message: "Other error")
26
+ expect(errors).to include hash_including(message: %q(#/ requires properties ["user"]))
27
+ expect(errors).to include hash_including(message: %q(#/settings requires properties ["name", "other"]))
28
+ end
29
+
30
+ it "flattens nested errors" do
31
+ schema = klass.any_of
32
+ errors = schema.fully_validate({ user: { settings: { name: 123 } }}, customize_errors: true)
33
+ expect(errors.length).to eq(3)
34
+ expect(errors).to include hash_including(message: "Nested")
35
+ end
36
+ end
@@ -0,0 +1,48 @@
1
+ module Examples
2
+ class CustomErrors
3
+ include JSON::SchemaBuilder
4
+
5
+ def required_error
6
+ lambda do |entities, error|
7
+ if error[:failed_attribute] == "Required"
8
+ required = entities.map(&:required).inject(:+).uniq
9
+ error[:message] = "#{error[:fragment]} requires properties #{required.map(&:to_s).inspect}"
10
+ end
11
+ end
12
+ end
13
+
14
+ def object_error
15
+ lambda do |entities, error|
16
+ error[:message] = "Custom object error"
17
+ end
18
+ end
19
+
20
+ def example
21
+ object error: required_error do
22
+ object :user, required: true, error: object_error
23
+ object :settings, error: required_error do
24
+ string :name, required: true, error: "Custom name error"
25
+ end
26
+ end
27
+ end
28
+
29
+ def example2
30
+ object do
31
+ object :settings, error: required_error do
32
+ string :other, required: true, error: "Other error"
33
+ end
34
+ end
35
+ end
36
+
37
+ def any_of
38
+ object do
39
+ entity :user do
40
+ any_of [
41
+ object { object(:settings) { string :name, error: "Nested" } },
42
+ null
43
+ ]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -15,7 +15,7 @@ RSpec.shared_context 'an entity' do
15
15
  end
16
16
 
17
17
  RSpec.shared_context 'an entity with a parent' do
18
- let(:parent){ OpenStruct.new children: [], required: [] }
18
+ let(:parent){ JSON::SchemaBuilder::Entity.new 'parent' }
19
19
  subject do
20
20
  JSON::SchemaBuilder::Entity.new 'name', title: 'test', parent: parent do
21
21
  schema[:evaluated_block] = true
@@ -92,7 +92,7 @@ RSpec.describe JSON::SchemaBuilder::Entity, type: :unit do
92
92
  subject.required = 'name'
93
93
  }.to change{
94
94
  parent.required
95
- }.to ['name']
95
+ }.from([]).to ['name']
96
96
  end
97
97
  end
98
98
 
@@ -1,66 +1,149 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe JSON::SchemaBuilder::Schema, type: :unit do
4
- let(:schema) do
5
- described_class.new a: 1, b: { c: 3 }, array: [123, { foo: 1, bar: { baz: 0 }}], anyOf: [
6
- { type: "null" },
7
- { type: "string" },
8
- {
9
- type: "object",
10
- properties: {
11
- one: { type: "string" },
12
- two: { type: "string" }
13
- }
14
- }
15
- ]
4
+ subject(:schema) do
5
+ Class.new do
6
+ include JSON::SchemaBuilder
7
+
8
+ def example
9
+ object do
10
+ integer :a
11
+
12
+ object :b do
13
+ integer :c
14
+ end
15
+
16
+ array :array do
17
+ items do
18
+ object do
19
+ integer :foo
20
+
21
+ object :bar do
22
+ integer :baz
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ entity :anything do
29
+ any_of [
30
+ null,
31
+ string,
32
+ object {
33
+ string :one
34
+ string :two
35
+ }
36
+ ]
37
+ end
38
+ end
39
+ end
40
+ end.new.example.schema
16
41
  end
17
42
 
18
43
  let(:other) do
19
- described_class.new a: 2, "b" => { d: 4 }, array: [456, { foo: 2, bar: { qux: 1 }}], anyOf: [
20
- { type: "null" },
21
- {
22
- type: "object",
23
- properties: {
24
- two: { type: "string" },
25
- three: { type: "string" }
26
- }
27
- }
28
- ]
44
+ Class.new do
45
+ include JSON::SchemaBuilder
46
+
47
+ def example
48
+ object do
49
+ integer :a2
50
+
51
+ object :b do
52
+ integer :d
53
+ end
54
+
55
+ array :array do
56
+ items do
57
+ object do
58
+ object :bar do
59
+ integer :qux
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ entity :anything do
66
+ any_of [
67
+ null,
68
+ object {
69
+ string :two
70
+ string :three
71
+ }
72
+ ]
73
+ end
74
+ end
75
+ end
76
+ end.new.example.schema
29
77
  end
30
78
 
31
79
  let(:merged) do
32
80
  {
33
- "a" => 2,
34
- "b" => {
35
- "c" => 3,
36
- "d" => 4
37
- },
38
- "array" => [
39
- 123, {
40
- "foo" => 1,
41
- "bar" => {
42
- "baz" => 0,
43
- }
81
+ "type" => :object,
82
+ "properties" => {
83
+ "a" => {
84
+ "type" => "integer"
44
85
  },
45
- 456, {
46
- "foo" => 2,
47
- "bar" => {
48
- "qux" => 1
49
- }
50
- }
51
- ],
52
- "anyOf" => [
53
- { "type" => "null" },
54
- { "type" => "string" },
55
- {
86
+ "a2" => {
87
+ "type" => "integer"
88
+ },
89
+ "b" => {
56
90
  "type" => "object",
57
91
  "properties" => {
58
- "one" => { "type" => "string" },
59
- "two" => { "type" => "string" },
60
- "three" => { "type" => "string" }
92
+ "c" => {
93
+ "type" => "integer"
94
+ },
95
+ "d" => {
96
+ "type" => "integer"
97
+ }
61
98
  }
99
+ },
100
+ "array" => {
101
+ "type" => "array",
102
+ "items" => {
103
+ "type" => "object",
104
+ "properties" => {
105
+ "foo" => {
106
+ "type" => "integer",
107
+ },
108
+ "bar" => {
109
+ "type" => "object",
110
+ "properties" => {
111
+ "baz" => {
112
+ "type" => "integer"
113
+ },
114
+ "qux" => {
115
+ "type" => "integer"
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ },
122
+ "anything" => {
123
+ "anyOf" => [
124
+ {
125
+ "type" => "null"
126
+ },
127
+ {
128
+ "type" => "string"
129
+ },
130
+ {
131
+ "type" => "object",
132
+ "properties" => {
133
+ "one" => {
134
+ "type" => "string"
135
+ },
136
+ "two" => {
137
+ "type" => "string"
138
+ },
139
+ "three" => {
140
+ "type" => "string"
141
+ }
142
+ }
143
+ }
144
+ ]
62
145
  }
63
- ]
146
+ }
64
147
  }
65
148
  end
66
149
 
@@ -93,4 +176,101 @@ RSpec.describe JSON::SchemaBuilder::Schema, type: :unit do
93
176
  expect{ schema.merge! other }.to_not change { other.data }
94
177
  end
95
178
  end
179
+
180
+ %w(validate validate! fully_validate).each do |validator|
181
+ describe "##{ validator }" do
182
+ it "should #{ validator }" do
183
+ expect(JSON::Validator).to receive(validator)
184
+ .with schema.as_json, { }, opts: true
185
+ schema.send validator, { }, opts: true
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "#fragments" do
191
+ context "with an unmerged schema" do
192
+ subject(:fragments) { schema.fragments }
193
+ its(:keys) do
194
+ is_expected.to match_array %w(
195
+ #/
196
+ #/a
197
+ #/b
198
+ #/b/c
199
+ #/array
200
+ #/array/foo
201
+ #/array/bar
202
+ #/array/bar/baz
203
+ #/anything
204
+ #/anything/one
205
+ #/anything/two
206
+ )
207
+ end
208
+
209
+ it "stores the schema by fragment" do
210
+ expect(fragments["#/"].first.as_json).to eq schema.as_json
211
+ end
212
+
213
+ it "structures the fragments correctly" do
214
+ expect(fragments.values.flatten).to all be_a JSON::SchemaBuilder::Entity
215
+ expect(fragments["#/"].length).to eq 1
216
+ expect(fragments["#/"].length).to eq 1
217
+ expect(fragments["#/a"].length).to eq 1
218
+ expect(fragments["#/b"].length).to eq 1
219
+ expect(fragments["#/b/c"].length).to eq 1
220
+ expect(fragments["#/array"].length).to eq 3
221
+ expect(fragments["#/array/foo"].length).to eq 1
222
+ expect(fragments["#/array/bar"].length).to eq 1
223
+ expect(fragments["#/array/bar/baz"].length).to eq 1
224
+ expect(fragments["#/anything"].length).to eq 4
225
+ expect(fragments["#/anything/one"].length).to eq 1
226
+ expect(fragments["#/anything/two"].length).to eq 1
227
+ end
228
+ end
229
+
230
+ context "with a merged schema" do
231
+ subject(:fragments) { schema.merge(other).fragments }
232
+ its(:keys) do
233
+ is_expected.to match_array %w(
234
+ #/
235
+ #/a
236
+ #/a2
237
+ #/b
238
+ #/b/c
239
+ #/b/d
240
+ #/array
241
+ #/array/foo
242
+ #/array/bar
243
+ #/array/bar/baz
244
+ #/array/bar/qux
245
+ #/anything
246
+ #/anything/one
247
+ #/anything/two
248
+ #/anything/three
249
+ )
250
+ end
251
+
252
+ it "stores the schema by fragment" do
253
+ expect(fragments["#/"].first.as_json).to eq schema.as_json
254
+ end
255
+
256
+ it "structures the fragments correctly" do
257
+ expect(fragments.values.flatten).to all be_a JSON::SchemaBuilder::Entity
258
+ expect(fragments["#/"].length).to eq 2
259
+ expect(fragments["#/a"].length).to eq 1
260
+ expect(fragments["#/a2"].length).to eq 1
261
+ expect(fragments["#/b"].length).to eq 2
262
+ expect(fragments["#/b/c"].length).to eq 1
263
+ expect(fragments["#/b/d"].length).to eq 1
264
+ expect(fragments["#/array"].length).to eq 6
265
+ expect(fragments["#/array/foo"].length).to eq 1
266
+ expect(fragments["#/array/bar"].length).to eq 2
267
+ expect(fragments["#/array/bar/baz"].length).to eq 1
268
+ expect(fragments["#/array/bar/qux"].length).to eq 1
269
+ expect(fragments["#/anything"].length).to eq 7
270
+ expect(fragments["#/anything/one"].length).to eq 1
271
+ expect(fragments["#/anything/two"].length).to eq 2
272
+ expect(fragments["#/anything/three"].length).to eq 1
273
+ end
274
+ end
275
+ end
96
276
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-schema_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Parrish
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-19 00:00:00.000000000 Z
11
+ date: 2018-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: '2'
117
+ version: '2.1'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '2'
124
+ version: '2.1'
125
125
  description: Build JSON schemas with Ruby
126
126
  email:
127
127
  - michael@zooniverse.org
@@ -139,11 +139,46 @@ files:
139
139
  - README.md
140
140
  - Rakefile
141
141
  - gemfiles/.bundle/config
142
- - gemfiles/activesupport_4.0.gemfile
143
- - gemfiles/activesupport_4.1.gemfile
144
- - gemfiles/activesupport_4.2.gemfile
145
- - gemfiles/activesupport_5.0.gemfile
146
- - gemfiles/activesupport_5.1.gemfile
142
+ - gemfiles/version_2.1.0_4.0.gemfile
143
+ - gemfiles/version_2.1.0_4.1.gemfile
144
+ - gemfiles/version_2.1.0_4.2.gemfile
145
+ - gemfiles/version_2.1.0_5.0.gemfile
146
+ - gemfiles/version_2.1.0_5.1.gemfile
147
+ - gemfiles/version_2.2.0_4.0.gemfile
148
+ - gemfiles/version_2.2.0_4.1.gemfile
149
+ - gemfiles/version_2.2.0_4.2.gemfile
150
+ - gemfiles/version_2.2.0_5.0.gemfile
151
+ - gemfiles/version_2.2.0_5.1.gemfile
152
+ - gemfiles/version_2.3.0_4.0.gemfile
153
+ - gemfiles/version_2.3.0_4.1.gemfile
154
+ - gemfiles/version_2.3.0_4.2.gemfile
155
+ - gemfiles/version_2.3.0_5.0.gemfile
156
+ - gemfiles/version_2.3.0_5.1.gemfile
157
+ - gemfiles/version_2.4.0_4.0.gemfile
158
+ - gemfiles/version_2.4.0_4.1.gemfile
159
+ - gemfiles/version_2.4.0_4.2.gemfile
160
+ - gemfiles/version_2.4.0_5.0.gemfile
161
+ - gemfiles/version_2.4.0_5.1.gemfile
162
+ - gemfiles/version_2.5.0_4.0.gemfile
163
+ - gemfiles/version_2.5.0_4.1.gemfile
164
+ - gemfiles/version_2.5.0_4.2.gemfile
165
+ - gemfiles/version_2.5.0_5.0.gemfile
166
+ - gemfiles/version_2.5.0_5.1.gemfile
167
+ - gemfiles/version_2.6.0_4.0.gemfile
168
+ - gemfiles/version_2.6.0_4.1.gemfile
169
+ - gemfiles/version_2.6.0_4.2.gemfile
170
+ - gemfiles/version_2.6.0_5.0.gemfile
171
+ - gemfiles/version_2.6.0_5.1.gemfile
172
+ - gemfiles/version_2.7.0_4.0.gemfile
173
+ - gemfiles/version_2.7.0_4.1.gemfile
174
+ - gemfiles/version_2.7.0_4.2.gemfile
175
+ - gemfiles/version_2.7.0_5.0.gemfile
176
+ - gemfiles/version_2.7.0_5.1.gemfile
177
+ - gemfiles/version_2.8.0_4.0.gemfile
178
+ - gemfiles/version_2.8.0_4.1.gemfile
179
+ - gemfiles/version_2.8.0_4.2.gemfile
180
+ - gemfiles/version_2.8.0_5.0.gemfile
181
+ - gemfiles/version_2.8.0_5.1.gemfile
147
182
  - json-schema_builder.gemspec
148
183
  - lib/json/schema_builder.rb
149
184
  - lib/json/schema_builder/any.rb
@@ -168,6 +203,7 @@ files:
168
203
  - lib/json/schema_builder/version.rb
169
204
  - spec/integration/array_definitions_spec.rb
170
205
  - spec/integration/context_delegation_spec.rb
206
+ - spec/integration/custom_errors_spec.rb
171
207
  - spec/integration/entity_literals_spec.rb
172
208
  - spec/integration/ids_spec.rb
173
209
  - spec/integration/mixed_arrays_spec.rb
@@ -186,6 +222,7 @@ files:
186
222
  - spec/support/attribute_matcher.rb
187
223
  - spec/support/examples/array_definitions.rb
188
224
  - spec/support/examples/context_delegation.rb
225
+ - spec/support/examples/custom_errors.rb
189
226
  - spec/support/examples/entity_literals.rb
190
227
  - spec/support/examples/ids.rb
191
228
  - spec/support/examples/mixed_arrays.rb
@@ -240,6 +277,7 @@ summary: Build JSON schemas with Ruby
240
277
  test_files:
241
278
  - spec/integration/array_definitions_spec.rb
242
279
  - spec/integration/context_delegation_spec.rb
280
+ - spec/integration/custom_errors_spec.rb
243
281
  - spec/integration/entity_literals_spec.rb
244
282
  - spec/integration/ids_spec.rb
245
283
  - spec/integration/mixed_arrays_spec.rb
@@ -258,6 +296,7 @@ test_files:
258
296
  - spec/support/attribute_matcher.rb
259
297
  - spec/support/examples/array_definitions.rb
260
298
  - spec/support/examples/context_delegation.rb
299
+ - spec/support/examples/custom_errors.rb
261
300
  - spec/support/examples/entity_literals.rb
262
301
  - spec/support/examples/ids.rb
263
302
  - spec/support/examples/mixed_arrays.rb