-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
397 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# frozen_string_literal: true | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Exception | ||
# This exception is supposed to be thrown whenever end user provides | ||
# malformed input - too many types, not enough types, not a type, etc. | ||
class ComplianceError < RuntimeError | ||
end | ||
end | ||
end | ||
end | ||
end |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# frozen_string_literal: true | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Type | ||
# Used as a wildcard to pass anything through | ||
class Any | ||
def hash | ||
self.class.hash | ||
end | ||
|
||
def eql?(*) | ||
true | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_String_literal: true | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
# Stores data about single type attribute | ||
class Attribute | ||
# @!attribute | ||
# @return [AMA::Entity::Mapper::Type::Concrete] | ||
attr_accessor :owner | ||
# @!attribute | ||
# @return [Symbol] | ||
attr_accessor :name | ||
# @!attribute type | ||
# @return [AMA::Entity::Mapper::Type::Proxy] | ||
attr_accessor :type | ||
# @!attribute virtual | ||
# @return [TrueClass, FalseClass] | ||
attr_accessor :virtual | ||
# @!attribute sensitive | ||
# @return [TrueClass, FalseClass] | ||
attr_accessor :sensitive | ||
# @!attribute nullable | ||
# @return [TrueClass, FalseClass] | ||
attr_accessor :nullable | ||
# @!attribute values Possible values this attribute can take | ||
# @return [Array] | ||
attr_accessor :values | ||
|
||
def initialize(owner, name, type, **options) | ||
@owner = owner | ||
@name = name | ||
@type = type | ||
@nullable = options.fetch(:nullable, true) | ||
@sensitive = options.fetch(:sensitive, false) | ||
@virtual = options.fetch(:virtual, false) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative '../exception/compliance_error' | ||
require_relative 'any' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Type | ||
# Used to describe concrete type (that means, class is already known, | ||
# though it's parameters may be resolved later). | ||
class Concrete | ||
# @!attribute type | ||
# @return [Class] | ||
attr_accessor :type | ||
# @!attribute parameters | ||
# @return [Hash{Symbol, AMA::Entity::Mapper::Type::Proxy}] | ||
attr_accessor :parameters | ||
# @!attribute attributes | ||
# @return [Hash{Symbol, AMA::Entity::Mapper::Type::Attribute}] | ||
attr_accessor :attributes | ||
|
||
def initialize(type) | ||
@type = type | ||
@parameters = {} | ||
@attributes = {} | ||
end | ||
|
||
def resolved? | ||
parameters.values.all?(&:resolved?) && | ||
attributes.values.map(&:type).all?(&:resolved?) | ||
end | ||
|
||
def parameter?(parameter) | ||
parameters.key?(parameter) | ||
end | ||
|
||
def resolve_parameter(parameter, substitution) | ||
unless parameter?(parameter) | ||
message = "Tried to resolve nonexistent parameter #{parameter} " \ | ||
"on type #{self}" | ||
compliance_error message | ||
end | ||
parameters[parameter] = substitution | ||
end | ||
|
||
def hash | ||
@type.hash ^ @parameters.hash ^ @attributes.hash | ||
end | ||
|
||
def eql?(other) | ||
return true if other.is_a?(Any) | ||
return false unless other.is_a?(self.class) | ||
@type == other.type && | ||
@parameters == other.parameters && | ||
@attributes == other.attributes | ||
end | ||
|
||
def to_s | ||
"Concrete type #{@type}" | ||
end | ||
|
||
private | ||
|
||
def compliance_error(message) | ||
raise ::AMA::Entity::Mapper::Exception::ComplianceError, message | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# frozen_string_literal: true | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Type | ||
# Used to fill in types that would be known later at runtime | ||
class Parameter | ||
attr_reader :owner | ||
attr_reader :id | ||
|
||
# @param [Class] owner | ||
# @param [Symbol] id | ||
def initialize(owner, id) | ||
@owner = owner | ||
@id = id | ||
end | ||
|
||
def to_s | ||
"Parameter :#{id} (declared in #{owner})" | ||
end | ||
|
||
def hash | ||
@owner.hash ^ @id.hash | ||
end | ||
|
||
def eql?(other) | ||
return false unless other.is_a?(self.class) | ||
id == other.id && owner == other.owner | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'parameter' | ||
require_relative 'any' | ||
require_relative 'concrete' | ||
require_relative '../exception/compliance_error' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Type | ||
# This class is used to serve as proxy for real types | ||
class Proxy | ||
attr_accessor :type | ||
|
||
def resolved? | ||
return true if type.is_a?(Any) | ||
return false if type.is_a?(Parameter) | ||
type.resolved? | ||
end | ||
|
||
def resolvable? | ||
type.is_a?(Concrete) | ||
end | ||
|
||
def resolve_parameter(parameter, substitution) | ||
if resolvable? | ||
return type.resolve_parameter(parameter, substitution) | ||
end | ||
message = "Tried to resolve #{self.class} type " \ | ||
"with parameter #{parameter} => #{substitution}" | ||
compliance_error(message) | ||
end | ||
|
||
def hash | ||
type.hash | ||
end | ||
|
||
def eql?(o) | ||
o.is_a?(self.class) && o.type == @type | ||
end | ||
|
||
private | ||
|
||
def compliance_error(message) | ||
raise ::AMA::Entity::Mapper::Exception::ComplianceError, message | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# frozen_String_literal: true | ||
|
||
require 'logger' | ||
|
||
module AMA | ||
module Entity | ||
class Mapper | ||
module Type | ||
# Holds all registered types | ||
class Registry | ||
attr_accessor :types | ||
|
||
def initialize | ||
@types = {} | ||
end | ||
|
||
# @param [AMA::Entity::Mapper::Type::Concrete] type | ||
def register(type) | ||
@types[type.type] = type | ||
end | ||
|
||
# @param [Class] klass | ||
def registered?(klass) | ||
@types.key?(klass) | ||
end | ||
|
||
def for(klass) | ||
find_class_types(klass) | find_module_types(klass) | ||
end | ||
|
||
private | ||
|
||
def inheritance_chain(klass) | ||
cursor = klass | ||
chain = [] | ||
loop do | ||
chain.push(cursor) | ||
cursor = cursor.superclass | ||
break if cursor.nil? | ||
end | ||
chain | ||
end | ||
|
||
def find_class_types(klass) | ||
inheritance_chain(klass).each_with_object([]) do |entry, carrier| | ||
carrier.push(types[entry]) if types[entry] | ||
end | ||
end | ||
|
||
def find_module_types(klass) | ||
chain = inheritance_chain(klass).reverse | ||
result = chain.reduce([]) do |carrier, entry| | ||
ancestor_types = entry.ancestors.map do |candidate| | ||
types[candidate] | ||
end | ||
carrier | ancestor_types.reject(&:nil?) | ||
end | ||
result.reverse | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |
Empty file.
Oops, something went wrong.