Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement type systems #149

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 38 additions & 17 deletions lib/surrealist.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

require 'oj'
require 'set'
require_relative 'surrealist/any'
require_relative 'surrealist/bool'
require_relative 'surrealist/builder'
require_relative 'surrealist/carrier'
require_relative 'surrealist/class_methods'
require_relative 'surrealist/copier'
require_relative 'surrealist/exception_raiser'
Expand All @@ -15,15 +12,20 @@
require_relative 'surrealist/schema_definer'
require_relative 'surrealist/serializer'
require_relative 'surrealist/string_utils'
require_relative 'surrealist/type_helper'
require_relative 'surrealist/result'
require_relative 'surrealist/type_systems'
require_relative 'surrealist/value_assigner'
require_relative 'surrealist/vars_helper'
require_relative 'surrealist/wrapper'
require_relative 'surrealist/configuration'

# Main module that provides the +json_schema+ class method and +surrealize+ instance method.
module Surrealist
# Default namespaces nesting level
DEFAULT_NESTING_LEVEL = 666
DEFAULT_NESTING_LEVEL = Configuration::DEFAULT_NESTING_LEVEL
# Expose Surrealist's builtin type system's types
Any = TypeSystems::Builtin::Types::Any
Bool = TypeSystems::Builtin::Types::Bool

class << self
# @param [Class] base class to include/extend +Surrealist+.
Expand Down Expand Up @@ -128,43 +130,62 @@ def build_schema(instance:, **args)
schema = Surrealist::VarsHelper.find_schema(instance.class)
Surrealist::ExceptionRaiser.raise_unknown_schema!(instance) if schema.nil?

parameters = config ? config.merge(args) : args
unless instance.class.type_system_override.nil?
args.merge!(type_system: instance.class.type_system_override)
end

overridden_config = config.with_overrides(args)

# TODO: Refactor (something pipeline-like would do here, perhaps a builder of some sort)
carrier = Surrealist::Carrier.call(parameters)
copied_schema = Surrealist::Copier.deep_copy(schema)
built_schema = Builder.new(carrier, copied_schema, instance).call
wrapped_schema = Surrealist::Wrapper.wrap(built_schema, carrier, instance.class.name)
carrier.camelize ? Surrealist::HashUtils.camelize_hash(wrapped_schema) : wrapped_schema
built_schema = Builder.new(overridden_config, copied_schema, instance).call
wrapped_schema = Surrealist::Wrapper.wrap(built_schema, overridden_config, instance.class.name)
overridden_config.camelize? ? Surrealist::HashUtils.camelize_hash(wrapped_schema) : wrapped_schema
end
# rubocop:enable Metrics/AbcSize

# Reads current default serialization arguments.
#
# @return [Hash] default arguments (@see Surrealist::Carrier)
def config
@default_args || Surrealist::HashUtils::EMPTY_HASH
@config || Configuration::DEFAULT
end

# Sets default serialization arguments with a block
#
# @param [Hash] hash arguments to be set (@see Surrealist::Carrier)
# @param [Hash] hash of arguments to be set (@see Surrealist::Carrier)
# @param [Proc] _block a block which will be yielded to Surrealist::Carrier instance
#
# @example set config
# Surrealist.configure do |config|
# config.camelize = true
# config.include_root = true
# end
def configure(hash = nil, &_block)
#
# rubocop:disable Metrics/MethodLength
def configure(config = nil, &_block)
if block_given?
carrier = Surrealist::Carrier.new
yield(carrier)
@default_args = carrier.parameters
Configuration.new.tap do |config_instance|
yield config_instance
@config = config_instance
end
else
@default_args = hash.nil? ? Surrealist::HashUtils::EMPTY_HASH : hash
@config =
if config.nil?
Configuration::DEFAULT
elsif config.is_a?(Hash)
Configuration.new(config)
elsif config.is_a?(Configuration)
config
else
raise ArgumentError, <<~MSG.squish
Expected `config` to be a hash, nil, or an instance of Surrealist::Configuration,
but got: #{config}
MSG
end
end
end
# rubocop:enable Metrics/MethodLength

private

Expand Down
4 changes: 0 additions & 4 deletions lib/surrealist/any.rb

This file was deleted.

4 changes: 0 additions & 4 deletions lib/surrealist/bool.rb

This file was deleted.

15 changes: 9 additions & 6 deletions lib/surrealist/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ class Builder
Schema = Struct.new(:key, :value)
Schema.freeze

# @param [Carrier] carrier instance of Surrealist::Carrier
# @param [Configuration] instance of Surrealist::Configuration
# @param [Hash] schema the schema defined in the object's class.
# @param [Object] instance the instance of the object which methods from the schema are called on.
def initialize(carrier, schema, instance)
@carrier = carrier
def initialize(config, schema, instance)
@config = config
@schema = schema
@instance = instance
end
Expand All @@ -30,8 +30,11 @@ def call(schema = @schema, instance = @instance)
if schema_value.is_a?(Hash)
check_for_ar(schema, instance, schema_key, schema_value)
else
ValueAssigner.assign(Schema.new(schema_key, schema_value),
instance) { |coerced_value| schema[schema_key] = coerced_value }
ValueAssigner.assign(
Schema.new(schema_key, schema_value),
instance,
config,
) { |coerced_value| schema[schema_key] = coerced_value }
end
end
rescue NoMethodError => e
Expand All @@ -40,7 +43,7 @@ def call(schema = @schema, instance = @instance)

private

attr_reader :carrier, :instance, :schema
attr_reader :config, :instance, :schema

# Checks if result is an instance of ActiveRecord::Relation
#
Expand Down
104 changes: 0 additions & 104 deletions lib/surrealist/carrier.rb

This file was deleted.

9 changes: 9 additions & 0 deletions lib/surrealist/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ def surrealize_with(klass, tag: Surrealist::VarsHelper::DEFAULT_TAG)
end
end

# A DSL method for overriding a type system for a specific serializer.
#
# @param [Class] system the type system to use. Must conform to Surrealist's type system
# interface.
def type_system(system)
@type_system_override = system
end
attr_reader :type_system_override

private

def read_schema
Expand Down
106 changes: 106 additions & 0 deletions lib/surrealist/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# frozen_string_literal: true

require_relative 'configuration/validator'
require_relative 'configuration/settings_helper'

module Surrealist
# Surrealist's main configuration class.
class Configuration
include SettingsHelper
extend Gem::Deprecate

DEFAULT_NESTING_LEVEL = 666

options :camelize,
:include_root,
:include_namespaces,
:root,
:namespace_nesting_level,
:type_system

alias camelize? camelize
alias include_root? include_root
alias include_namespaces? include_namespaces

# Aliases for backward compatibility.
alias namespaces_nesting_level namespace_nesting_level
deprecate :namespaces_nesting_level, 'namespace_nesting_level', 2020, 1

def initialize(**args)
# Alias for backward compatibility.
namespace_nesting_level = args[:namespace_nesting_level] || args[:namespaces_nesting_level]

@camelize = args.fetch(:camelize, false)
@include_root = args.fetch(:include_root, false)
@include_namespaces = args.fetch(:include_namespaces, false)

self.root = args.fetch(:root, nil)
self.namespace_nesting_level = namespace_nesting_level
self.type_system = args.fetch(:type_system, nil)

super
end

def root=(value)
set_option(
:root,
if value.is_a?(String)
value.strip
else
value
end,
)
end

def namespace_nesting_level=(value)
set_option(:namespace_nesting_level, value || DEFAULT_NESTING_LEVEL)
end
alias namespaces_nesting_level= namespace_nesting_level=
deprecate :namespaces_nesting_level=, 'namespace_nesting_level=', 2020, 1

def type_system=(value)
set_option(
:type_system,
case value
when nil then Surrealist::TypeSystems::Builtin # default
when :builtin then Surrealist::TypeSystems::Builtin
when :dry_types then Surrealist::TypeSystems::DryTypes
else
value
end,
)
end

def default?
self == DEFAULT
end

def ==(other)
settings == other.settings
end

def with_overrides(**overrides)
return self if overrides.empty?

# Alias for backward compatibility.
if overrides.key?(:namespaces_nesting_level)
overrides.merge!(namespace_nesting_level: overrides.delete(:namespaces_nesting_level))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance/RedundantMerge: Use overrides[:namespace_nesting_level] = overrides.delete(:namespaces_nesting_level) instead of overrides.merge!(namespace_nesting_level: overrides.delete(:namespaces_nesting_level)).

end

self.class.new(settings.merge(overrides))
end

def settings
{
camelize: camelize,
include_root: include_root,
include_namespaces: include_namespaces,
root: root,
namespace_nesting_level: namespace_nesting_level,
type_system: type_system,
}
end
end

Configuration::DEFAULT = Configuration.new.freeze
end
Loading