json-schema_builder 0.4.0 → 0.5.0

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