Skip to content

Commit

Permalink
Merge remote-tracking branches 'origin/node', 'origin/new', 'origin/p…
Browse files Browse the repository at this point in the history
…roperty_accessor', 'origin/slash', 'origin/described_by', 'origin/metaschema_instance_modules', 'origin/registration', 'origin/super', 'origin/ptr', 'origin/uri', 'origin/json_schema_test_suite', 'origin/yardopts', 'origin/misc' and 'origin/dependabot/github_actions/coverallsapp/github-action-2.1.2' into HEAD

node                   #274
new                     #280
property_accessor        #264
slash                     #273
described_by               #276
metaschema_instance_modules #271
registration               #286
super                     #292
ptr                      #293
uri                     #278
json_schema_test_suite #275
yardopts              #283
misc                 #284
dependabot/github_actions/coverallsapp/github-action-2.1.2 #291
  • Loading branch information
notEthan committed May 29, 2023
14 parents 8b855c2 + 1f7e287 + 1c7ae12 + 762c8a5 + 9a9692b + 601661a + 4436cde + 6beb296 + 0f8b847 + 35476fe + d08d1bd + 23d0ad0 + c660cfd + 4f09a0f commit 0ea3cdb
Show file tree
Hide file tree
Showing 44 changed files with 738 additions and 360 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ${{ matrix.runs-on }}

env:
BUNDLE_WITHOUT: dev # ruby/setup-ruby's invocation of bundle install will be without the `dev` group
BUNDLE_WITHOUT: dev doc # ruby/setup-ruby's invocation of bundle install will be without these groups

steps:

Expand All @@ -37,7 +37,7 @@ jobs:
- run: bundle exec rake test

- name: Report to Coveralls
uses: coverallsapp/github-action@1.1.3
uses: coverallsapp/github-action@v2.1.2
with:
github-token: ${{ secrets.github_token }}
flag-name: "test ruby: ${{ matrix.ruby-version }} os: ${{ matrix.runs-on }}"
Expand All @@ -49,7 +49,7 @@ jobs:
steps:

- name: Report completion to Coveralls
uses: coverallsapp/github-action@1.1.3
uses: coverallsapp/github-action@v2.1.2
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
7 changes: 6 additions & 1 deletion .yardopts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
--main README.md --markup=markdown --no-private {lib}/**/*.rb
--main README.md
--markup=markdown
--markup-provider=commonmarker
--no-private
--hide-void-return
{lib}/**/*.rb
14 changes: 13 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ gem 'gig'

group(:dev) do
gem 'irb'
platform(:mri) { gem 'debug' }
platform(:mri) do
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
gem 'debug', '> 1'
else
gem 'byebug'
end
end
end

group(:test) do
Expand All @@ -24,4 +30,10 @@ group(:extdep) do
gem 'scorpio', '~> 0.6'
gem 'spreedly_openapi', github: 'notEthan/spreedly_openapi', tag: 'v0.2.0'
gem 'activesupport'
gem 'hashie'
end

group(:doc) do
gem 'yard'
platform(:mri, :truffleruby) { gem 'commonmarker' }
end
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

JSI offers an Object-Oriented representation for JSON data using JSON Schemas. Given your JSON Schemas, JSI constructs Ruby modules and classes which are used to instantiate your JSON data. These modules let you use JSON with all the niceties of OOP such as property accessors and application-defined instance methods.

To learn more about JSON Schema see [https://json-schema.org/](https://json-schema.org/).
To learn more about JSON Schema see <https://json-schema.org/>.

JSI marries object-oriented programming with JSON Schemas by associating a module with each schema, and extending every instance described by a schema with that module. When an application adds methods to a schema module, those methods can be used on its instances.

Expand Down
5 changes: 4 additions & 1 deletion Rakefile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
class JSITestTask < Rake::TestTask
def initialize(name: , title: , description: nil, pattern: nil, test_files: nil, env: {})
@title = title
@env = env.merge('JSI_TEST_TASK' => "test:#{name}")
@env = {}
@env['JSI_TEST_TASK'] = "test:#{name}"
@env['JSI_TESTREPORT'] = 'progress' if !ENV['JSI_TESTREPORT'] && !ENV['CI']
@env.update(env)
super(name) do |t|
t.description = description
t.pattern = pattern
Expand Down
8 changes: 8 additions & 0 deletions bin/c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env ruby

# c is for console, that's good enough for me

require_relative "../test/jsi_helper"

require "irb"
IRB.start(__FILE__)
4 changes: 1 addition & 3 deletions jsi.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "jsi/version"
require_relative "lib/jsi/version"

Gem::Specification.new do |spec|
spec.name = "jsi"
Expand Down
57 changes: 36 additions & 21 deletions lib/jsi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ module JSI
class Bug < NotImplementedError
end

# @private TODO remove, any ruby without this is already long EOL
FrozenError = Object.const_defined?(:FrozenError) ? ::FrozenError : Class.new(StandardError)

# @private
ROOT_PATH = Pathname.new(__FILE__).dirname.parent.expand_path

Expand All @@ -33,6 +36,7 @@ class Bug < NotImplementedError
autoload :Base, 'jsi/base'
autoload :Metaschema, 'jsi/metaschema'
autoload :MetaschemaNode, 'jsi/metaschema_node'
autoload :SchemaModule, 'jsi/schema_classes'
autoload :SchemaClasses, 'jsi/schema_classes'
autoload :SchemaRegistry, 'jsi/schema_registry'
autoload :Validation, 'jsi/validation'
Expand All @@ -44,35 +48,46 @@ class Bug < NotImplementedError

autoload :SimpleWrap, 'jsi/simple_wrap'

# instantiates a given schema object as a JSI Schema.
#
# see {JSI::Schema.new_schema}
#
# @param (see JSI::Schema.new_schema)
# @return (see JSI::Schema.new_schema)
def self.new_schema(schema_object, **kw)
JSI::Schema.new_schema(schema_object, **kw)
# (see JSI::Schema.new_schema)
def self.new_schema(schema_content,
# params of Schema.new_schema have their default values repeated here. delegating in a splat
# would remove repetition, but yard doesn't display delegated defaults with its (see X) directive.
default_metaschema: nil,
uri: nil,
stringify_symbol_keys: true
)
JSI::Schema.new_schema(schema_content,
default_metaschema: default_metaschema,
uri: uri,
stringify_symbol_keys: stringify_symbol_keys,
)
end

# instantiates a given schema object as a JSI Schema and returns its JSI Schema Module.
# Instantiates the given schema content as a JSI Schema, passing all params to
# {JSI.new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
#
# shortcut to chain {JSI::Schema.new_schema} + {Schema#jsi_schema_module}.
#
# @param (see JSI::Schema.new_schema)
# @return [Module, JSI::SchemaModule] the JSI Schema Module of the schema
def self.new_schema_module(schema_object, **kw)
JSI::Schema.new_schema(schema_object, **kw).jsi_schema_module
# @return [Module + JSI::SchemaModule]
def self.new_schema_module(schema_content, **kw)
JSI::Schema.new_schema(schema_content, **kw).jsi_schema_module
end

# `JSI.schema_registry` is the {JSI::SchemaRegistry} in which schemas are registered.
#
# @return [JSI::SchemaRegistry]
def self.schema_registry
return @schema_registry if instance_variable_defined?(:@schema_registry)
@schema_registry = SchemaRegistry.new
@schema_registry
end
end

JSI.schema_registry.autoload_uri("http://json-schema.org/draft-04/schema") { JSI::JSONSchemaOrgDraft04.schema }
JSI.schema_registry.autoload_uri("http://json-schema.org/draft-06/schema") { JSI::JSONSchemaOrgDraft06.schema }
JSI.schema_registry.autoload_uri("http://json-schema.org/draft-07/schema") { JSI::JSONSchemaOrgDraft07.schema }
# @param schema_registry [JSI::SchemaRegistry]
def self.schema_registry=(schema_registry)
@schema_registry = schema_registry
end

DEFAULT_SCHEMA_REGISTRY = SchemaRegistry.new.tap do |schema_registry|
schema_registry.autoload_uri("http://json-schema.org/draft-04/schema") { JSI::JSONSchemaOrgDraft04.schema }
schema_registry.autoload_uri("http://json-schema.org/draft-06/schema") { JSI::JSONSchemaOrgDraft06.schema }
schema_registry.autoload_uri("http://json-schema.org/draft-07/schema") { JSI::JSONSchemaOrgDraft07.schema }
end.freeze

self.schema_registry = DEFAULT_SCHEMA_REGISTRY.dup
end
54 changes: 40 additions & 14 deletions lib/jsi/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ def initialize(jsi_document,

jsi_initialize_memos

super()

self.jsi_document = jsi_document
self.jsi_ptr = jsi_ptr
if @jsi_ptr.root?
Expand Down Expand Up @@ -199,8 +201,6 @@ def jsi_node_content

# yields a JSI of each node at or below this one in this JSI's document.
#
# returns an Enumerator if no block is given.
#
# @yield [JSI::Base] each descendent node, starting with self
# @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
def jsi_each_descendent_node(&block)
Expand Down Expand Up @@ -319,6 +319,24 @@ def jsi_descendent_node(ptr)
descendent
end

# A shorthand alias for {#jsi_descendent_node}.
#
# Note that, though more convenient to type, using an operator whose meaning may not be intuitive
# to a reader could impair readability of code.
#
# examples:
#
# my_jsi / ['foo', 'bar']
# my_jsi / %w(foo bar)
# my_schema / JSI::Ptr['additionalProperties']
# my_schema / %w(properties foo items additionalProperties)
#
# @param (see #jsi_descendent_node)
# @return (see #jsi_descendent_node)
def /(ptr)
jsi_descendent_node(ptr)
end

# yields each token (array index or hash key) identifying a child node.
# yields nothing if this node is not complex or has no children.
#
Expand Down Expand Up @@ -426,8 +444,7 @@ def jsi_default_child(token, as_jsi: :auto)
# - the result is a complex value (responds to #to_ary or #to_hash)
# - the result is a schema (including true/false schemas)
#
# a plain value is returned when no schemas are known to describe the instance, or when the value is a
# simple type (anything unresponsive to #to_ary / #to_hash).
# The plain content is returned when it is a simple type.
#
# - true: the result value will always be returned as a JSI. the {#jsi_schemas} of the result may be
# empty if no schemas describe the instance.
Expand All @@ -449,7 +466,7 @@ def jsi_default_child(token, as_jsi: :auto)
# defaults are specified across those schemas), nil is returned.
# (one exception is when this JSI's instance is a Hash with a default or default_proc, which has
# unspecified behavior.)
# @return [JSI::Base, Object] the child value identified by the subscript token
# @return [JSI::Base, Object, nil] the child value identified by the subscript token
def [](token, as_jsi: :auto, use_default: true)
# note: overridden by Base::HashNode, Base::ArrayNode
jsi_simple_node_child_error(token)
Expand Down Expand Up @@ -477,6 +494,20 @@ def jsi_schema_modules
Util.ensure_module_set(jsi_schemas.map(&:jsi_schema_module))
end

# Is this JSI described by the given schema (or schema module)?
#
# @param schema [Schema, SchemaModule]
# @return [Boolean]
def described_by?(schema)
if schema.is_a?(Schema)
jsi_schemas.include?(schema)
elsif schema.is_a?(SchemaModule)
jsi_schema_modules.include?(schema)
else
raise(TypeError, "expected a Schema or Schema Module; got: #{schema.pretty_inspect.chomp}")
end
end

# yields the content of this JSI's instance. the block must result in
# a modified copy of the yielded instance (not modified in place, which would alter this JSI
# as well) which will be used to instantiate and return a new JSI with the modified content.
Expand All @@ -488,17 +519,11 @@ def jsi_schema_modules
# in a nondestructively modified copy of this.
# @return [JSI::Base subclass] the modified copy of self
def jsi_modified_copy(&block)
if @jsi_ptr.root?
modified_document = @jsi_ptr.modified_document_copy(@jsi_document, &block)
jsi_indicated_schemas.new_jsi(modified_document,
uri: jsi_schema_base_uri,
modified_jsi_root_node = @jsi_root_node.jsi_indicated_schemas.new_jsi(modified_document,
uri: @jsi_root_node.jsi_schema_base_uri,
)
else
modified_jsi_root_node = @jsi_root_node.jsi_modified_copy do |root|
@jsi_ptr.modified_document_copy(root, &block)
end
modified_jsi_root_node.jsi_descendent_node(@jsi_ptr)
end
end

# Is the instance an array?
Expand Down Expand Up @@ -615,7 +640,8 @@ def as_json(*opt)
Util.as_json(jsi_instance, *opt)
end

# an opaque fingerprint of this JSI for {Util::FingerprintHash}.
# see {Util::Private::FingerprintHash}
# @api private
def jsi_fingerprint
{
class: jsi_class,
Expand Down
10 changes: 4 additions & 6 deletions lib/jsi/base/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def to_a(**kw)
each(**kw) do |e|
ary << e
end
ary
ary.freeze
end

alias_method :entries, :to_a
Expand Down Expand Up @@ -100,8 +100,6 @@ def [](token, as_jsi: :auto, use_default: true)
#
# each yielded key is a key of the instance hash, and each yielded value is the result of {Base#[]}.
#
# returns an Enumerator if no block is given.
#
# @param kw keyword arguments are passed to {Base#[]}
# @yield [Object, Object] each key and value of this hash node
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
Expand All @@ -119,7 +117,9 @@ def each(**kw, &block)
# @param kw keyword arguments are passed to {Base#[]}
# @return [Hash]
def to_hash(**kw)
{}.tap { |h| jsi_node_content_hash_pubsend(:each_key) { |k| h[k] = self[k, **kw] } }
hash = {}
jsi_node_content_hash_pubsend(:each_key) { |k| hash[k] = self[k, **kw] }
hash.freeze
end

include Util::Hashlike
Expand Down Expand Up @@ -233,8 +233,6 @@ def [](token, as_jsi: :auto, use_default: true)
#
# each yielded element is the result of {Base#[]} for each index of the instance array.
#
# returns an Enumerator if no block is given.
#
# @param kw keyword arguments are passed to {Base#[]}
# @yield [Object] each element of this array node
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
Expand Down
Loading

0 comments on commit 0ea3cdb

Please sign in to comment.