morpher-0.2.6/0000755000175000017500000000000013715565705014431 5ustar debbiecocoadebbiecocoamorpher-0.2.6/README.md0000644000175000017500000000310713715565705015711 0ustar debbiecocoadebbiecocoamorpher ======= [![Build Status](https://secure.travis-ci.org/mbj/morpher.png?branch=master)](http://travis-ci.org/mbj/morpher) [![Dependency Status](https://gemnasium.com/mbj/morpher.png)](https://gemnasium.com/mbj/morpher) [![Code Climate](https://codeclimate.com/github/mbj/morpher.png)](https://codeclimate.com/github/mbj/morpher) Morpher is a data transformation algebra with optional tracked evaluation. It can be used at various places: * Domain to JSON and vice versa, for building rest style APIS * Domain to document db and vice versa, for building mappers * Form processing * ... Status ------ This library is in "moving to MDD from spike mode". ### Mutation coverage I use so called "implicit coverage". A term that was invented by the [rom](https://github.com/rom-rb)-team during mutation testing. Later when this library is not under steady flux anymore I'll switch to explicit coverage. Installation ------------ Install the gem `morpher` via your preferred method. Examples -------- See specs, Public Evaluator API is stable and there are ongoing 0.x.y releases for early adopters. Credits ------- * [mbj](https://github.com/mbj) Contributing ------------ * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with Rakefile or version (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. License ------- See LICENSE file. morpher-0.2.6/.gitignore0000644000175000017500000000005213715565705016416 0ustar debbiecocoadebbiecocoa/.rbx /Gemfile.lock /tmp /.bundle /vendor morpher-0.2.6/.circle.yml0000644000175000017500000000011513715565705016470 0ustar debbiecocoadebbiecocoa--- ruby: version: 1.9.3-p429 test: override: - bundle exec rake ci morpher-0.2.6/config/0000755000175000017500000000000013715565705015676 5ustar debbiecocoadebbiecocoamorpher-0.2.6/config/reek.yml0000644000175000017500000000470313715565705017353 0ustar debbiecocoadebbiecocoa--- UncommunicativeParameterName: accept: [] exclude: [] enabled: true reject: - !ruby/regexp /^.$/ - !ruby/regexp /[0-9]$/ - !ruby/regexp /[A-Z]/ TooManyMethods: max_methods: 10 exclude: - Morpher::Printer # 16 methods enabled: true max_instance_variables: 2 UncommunicativeMethodName: accept: ['s'] exclude: [] enabled: true reject: - !ruby/regexp /^[a-z]$/ - !ruby/regexp /[0-9]$/ - !ruby/regexp /[A-Z]/ LongParameterList: max_params: 3 # TODO: decrease max_params to 2 exclude: [] enabled: true overrides: {} FeatureEnvy: exclude: # False positives: - Morpher::Printer::Mixin::InstanceMethods#description - Morpher::Evaluator::Transformer::Domain::AttributeHash::Dump#call - Morpher::Evaluator::Transformer::Domain::AttributeAccessors::Load#call - Morpher::Evaluator::Transformer::Domain::InstanceVariables::Load#call - Morpher::Evaluator::Transformer::Domain::InstanceVariables::Dump#call enabled: true ClassVariable: exclude: [] enabled: true BooleanParameter: exclude: [] enabled: true IrresponsibleModule: exclude: # False positives - Morpher::Compiler - Morpher::Compiler::Preprocessor - Morpher::Compiler::Evaluator enabled: true UncommunicativeModuleName: accept: [] exclude: [] enabled: true reject: - !ruby/regexp /^.$/ - !ruby/regexp /[0-9]$/ NestedIterators: ignore_iterators: [] exclude: [] enabled: true max_allowed_nesting: 2 TooManyStatements: max_statements: 7 # TODO: decrease max_statements to 5 or less exclude: - Morpher::Compiler::Emitter#self.children enabled: true DuplicateMethodCall: allow_calls: [] exclude: [] enabled: false # TOOD enable max_calls: 1 UtilityFunction: max_helper_calls: 1 exclude: - Morpher::Evaluator::Predicate::Contradiction#inverse - Morpher::Evaluator::Predicate::Tautology#inverse - Morpher::Evaluator::Transformer::Coerce::ParseIso8601DateTime#invoke - Morpher::Evaluator::Transformer::Domain::AttributeHash::Dump#call enabled: true Attribute: exclude: [] enabled: false UncommunicativeVariableName: accept: ['_'] exclude: [] enabled: true reject: - !ruby/regexp /^.$/ - !ruby/regexp /[0-9]$/ - !ruby/regexp /[A-Z]/ RepeatedConditional: exclude: [] enabled: true max_ifs: 2 DataClump: exclude: [] enabled: true max_copies: 1 min_clump_size: 3 ControlParameter: exclude: [] enabled: true LongYieldList: max_params: 1 exclude: [] enabled: true NilCheck: exclude: [] morpher-0.2.6/config/devtools.yml0000644000175000017500000000003313715565705020254 0ustar debbiecocoadebbiecocoa--- unit_test_timeout: 1.0 morpher-0.2.6/config/yardstick.yml0000644000175000017500000000002313715565705020411 0ustar debbiecocoadebbiecocoa--- threshold: 100 morpher-0.2.6/config/flog.yml0000644000175000017500000000005513715565705017350 0ustar debbiecocoadebbiecocoa--- threshold: 27.0 # TODO: decrease to ~ 10 morpher-0.2.6/config/flay.yml0000644000175000017500000000004313715565705017351 0ustar debbiecocoadebbiecocoa--- threshold: 23 total_score: 427 morpher-0.2.6/config/heckle.yml0000644000175000017500000000005613715565705017655 0ustar debbiecocoadebbiecocoa--- library: adamantium namespace: Adamantium morpher-0.2.6/config/mutant.yml0000644000175000017500000000026013715565705017727 0ustar debbiecocoadebbiecocoaname: morpher namespace: Morpher zombie: true expect_coverage: 71.21 ignore_subjects: # Infinite runtime mutations here - Morpher::TypeLookup - Morpher::Compiler::Preprocessor morpher-0.2.6/config/rubocop.yml0000644000175000017500000000613113715565705020073 0ustar debbiecocoadebbiecocoainherit_from: ../.rubocop.yml # Avoid parameter lists longer than five parameters. ParameterLists: Max: 3 CountKeywordArgs: true # Avoid more than `Max` levels of nesting. BlockNesting: Max: 3 # Align with the style guide. CollectionMethods: PreferredMethods: collect: 'map' inject: 'reduce' find: 'detect' find_all: 'select' # Do not force public/protected/private keyword to be indented at the same # level as the def keyword. My personal preference is to outdent these keywords # because I think when scanning code it makes it easier to identify the # sections of code and visually separate them. When the keyword is at the same # level I think it sort of blends in with the def keywords and makes it harder # to scan the code and see where the sections are. AccessModifierIndentation: Enabled: false # Limit line length LineLength: Max: 120 # Disable documentation checking until a class needs to be documented once Documentation: Enabled: false # Do not always use &&/|| instead of and/or. AndOr: Enabled: false # Do not favor modifier if/unless usage when you have a single-line body IfUnlessModifier: Enabled: false # Allow case equality operator (in limited use within the specs) CaseEquality: Enabled: false # Constants do not always have to use SCREAMING_SNAKE_CASE ConstantName: Enabled: false # Not all trivial readers/writers can be defined with attr_* methods TrivialAccessors: Enabled: false # Allow empty lines around class body EmptyLinesAroundClassBody: Enabled: false # Allow empty lines around module body EmptyLinesAroundModuleBody: Enabled: false # Allow empty lines around block body EmptyLinesAroundBlockBody: Enabled: false # Allow multiple line operations to not require indentation MultilineOperationIndentation: Enabled: false # Prefer String#% over Kernel#sprintf FormatString: Enabled: false # Use square brackets for literal Array objects PercentLiteralDelimiters: PreferredDelimiters: '%': '{}' '%i': '[]' '%q': () '%Q': () '%r': '{}' '%s': () '%w': '[]' '%W': '[]' '%x': () # Use %i[...] for arrays of symbols SymbolArray: Enabled: true # Align if/else blocks with the variable assignment EndAlignment: AlignWith: variable # Do not always align parameters when it is easier to read AlignParameters: Exclude: - spec/**/*_spec.rb # Prefer #kind_of? over #is_a? ClassCheck: EnforcedStyle: kind_of? # Do not prefer double quotes to be used when %q or %Q is more appropriate UnneededPercentQ: Enabled: false # Allow a maximum ABC score Metrics/AbcSize: Max: 21.02 # Do not prefer lambda.call(...) over lambda.(...) LambdaCall: Enabled: false # Buggy cop, returns false positive for our code base NonLocalExitFromIterator: Enabled: false # To allow alignment of similar expressions we want to allow more than one # space around operators: # # let(:a) { bar + something } # let(:b) { foobar + something } # SpaceAroundOperators: Enabled: false # We use parallel assignments with great success ParallelAssignment: Enabled: false # Allow additional specs ExtraSpacing: Enabled: false morpher-0.2.6/examples/0000755000175000017500000000000013715565705016247 5ustar debbiecocoadebbiecocoamorpher-0.2.6/examples/README.md0000644000175000017500000000060713715565705017531 0ustar debbiecocoadebbiecocoaMorpher Examples ================ These are some random examples I captured on parings. At this early stage the examples are very "unimpressive" and only scratch the surface. The examples are used for parings to show API in action under IRB: ``` cd morpher bundle exec irb -I lib -r ./examples/your_example.rb # Now access the constants defined in example to play with the evaluators ``` morpher-0.2.6/examples/a.rb0000644000175000017500000000066313715565705017021 0ustar debbiecocoadebbiecocoarequire 'morpher' extend Morpher::NodeHelpers class Input include Anima.new(:foo) end # Input node = s(:block, s(:guard, s(:primitive, Hash)), s(:hash_transform, s(:key_symbolize, :foo, s(:guard, s(:or, s(:primitive, String), s(:primitive, NilClass) ) ) ) ), s(:load_attribute_hash, s(:param, Input)) ) EVALUATOR = Morpher.compile(node) morpher-0.2.6/examples/b.rb0000644000175000017500000000116013715565705017013 0ustar debbiecocoadebbiecocoarequire 'morpher' extend Morpher::NodeHelpers class Address include Anima.new(:street) end class Person include Anima.new(:address) end node = s(:block, s(:guard, s(:primitive, Hash)), s(:hash_transform, s(:key_symbolize, 'street', s(:guard, s(:primitive, String)) ) ), s(:load_attribute_hash, s(:param, Address)) ) ADDRESS_EVALUATOR = Morpher.compile(node) node = s(:block, s(:guard, s(:primitive, Hash)), s(:hash_transform, s(:key_symbolize, 'address', ADDRESS_EVALUATOR.node ) ), s(:load_attribute_hash, s(:param, Person)) ) PERSON_EVALUATOR = Morpher.compile(node) morpher-0.2.6/morpher.gemspec0000644000175000017500000000216513715565705017456 0ustar debbiecocoadebbiecocoaGem::Specification.new do |gem| gem.name = 'morpher' gem.version = '0.2.6' gem.authors = ['Markus Schirp'] gem.email = ['mbj@schirp-dso.com'] gem.description = 'Composeable predicates on POROs' gem.summary = gem.description gem.homepage = 'https://github.com/mbj/morpher' gem.license = 'MIT' gem.require_paths = %w[lib] gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- spec/{unit,integration}`.split("\n") gem.extra_rdoc_files = %w[LICENSE] gem.license = 'MIT' gem.required_ruby_version = ['>= 2.1'] gem.add_runtime_dependency('abstract_type', '~> 0.0.7') gem.add_runtime_dependency('adamantium', '~> 0.2.0') gem.add_runtime_dependency('anima', '~> 0.3.0') gem.add_runtime_dependency('ast', '~> 2.2') gem.add_runtime_dependency('concord', '~> 0.1.5') gem.add_runtime_dependency('equalizer', '~> 0.0.9') gem.add_runtime_dependency('ice_nine', '~> 0.11.0') gem.add_runtime_dependency('procto', '~> 0.0.2') gem.add_development_dependency('devtools', '~> 0.1.3') end morpher-0.2.6/.rspec0000644000175000017500000000007013715565705015543 0ustar debbiecocoadebbiecocoa--color --warnings --order random --require spec_helper morpher-0.2.6/LICENSE0000644000175000017500000000204113715565705015433 0ustar debbiecocoadebbiecocoaCopyright (c) 2013 Markus Schirp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. morpher-0.2.6/circle.yml0000644000175000017500000000012413715565705016412 0ustar debbiecocoadebbiecocoa--- machine: ruby: version: 2.2.3 test: override: - bundle exec rake ci morpher-0.2.6/.rubocop.yml0000644000175000017500000000021213715565705016676 0ustar debbiecocoadebbiecocoaAllCops: Include: - 'Gemfile' Exclude: - 'Gemfile.devtools' - 'vendor/**/*' - 'examples/**/*' - 'benchmarks/**/*' morpher-0.2.6/lib/0000755000175000017500000000000013715565705015177 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/0000755000175000017500000000000013715565705016653 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluation.rb0000644000175000017500000000444513715565705021356 0ustar debbiecocoadebbiecocoamodule Morpher # Abstract namespace class for evaluation states class Evaluation include AbstractType, Printer::Mixin, Adamantium::Flat, Anima.new( :evaluator, :input, :output, :success ) private :success # Test if evaluation was successful # # @return [true] # if evaluation was successful # # @return [false] # otherwise # # @api private # alias_method :success?, :success public :success? ERROR_DEFAULTS = IceNine.deep_freeze( output: Undefined, success: false ) SUCCESS_DEFAULTS = IceNine.deep_freeze( success: true ) # Return error instance # # @param [Hash] attributes # # @return [Evaluation] # # @api private # def self.error(attributes) new(ERROR_DEFAULTS.merge(attributes)) end # Return successful instance # # @param [Hash] attributes # # @return [Evaluation] # # @api private # def self.success(attributes) new(SUCCESS_DEFAULTS.merge(attributes)) end # Evaluation state for nullary evaluators class Nullary < self printer do name indent do attributes :input, :output, :success? visit :evaluator end end end # Evaluation state for nary evaluators class Nary < self include anima.add(:evaluations) printer do name indent do attributes :input, :output, :success? attribute_class :evaluator visit_many :evaluations end end end # Evaluation # Evaluation state for unary evaluators class Binary < self include anima.add(:left_evaluation, :right_evaluation) printer do name indent do attributes :input, :output, :success? visit :left_evaluation visit :right_evaluation end end end # Unary # Evaluation state for unary evaluators class Unary < self include anima.add(:operand_evaluation) printer do name indent do attributes :input, :output, :success? visit :operand_evaluation visit :evaluator end end end # Unary end # Evaluation end # Morpher morpher-0.2.6/lib/morpher/registry.rb0000644000175000017500000000167613715565705021062 0ustar debbiecocoadebbiecocoamodule Morpher # Mixin for to provide a node registry module Registry # Hook called when module is included # # @param [Module, Class] descendant # # @return [undefined] # # @api private # def self.included(descendant) descendant.const_set(:REGISTRY, {}) descendant.class_eval do extend ClassMethods end end # Return node type # # @return [Symbol] # # @api private # def type self.class::TYPE end # Methods to get mixed in at singleton level module ClassMethods # Register evaluator under name # # TODO: Disallow duplicate registration under same name # # @param [Symbol] name # # @return [undefined] # # @api private # def register(name) const_set(:TYPE, name) self::REGISTRY[name] = self end end # ClassMethods end # Registry end # Morpher morpher-0.2.6/lib/morpher/compiler.rb0000644000175000017500000000040413715565705021010 0ustar debbiecocoadebbiecocoamodule Morpher # Abstract compiler base class class Compiler include AbstractType # Call compiler # # @param [Node] node # # @return [Object] # # @api private # abstract_method :call end # Compiler end # Morpher morpher-0.2.6/lib/morpher/evaluator/0000755000175000017500000000000013715565705020655 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluator/nary.rb0000644000175000017500000000370013715565705022153 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Mixin for nary evaluators module Nary CONCORD = Concord::Public.new(:body) PRINTER = lambda do |_| name indent do visit_many(:body) end end # Return AST # # @return [AST::Node] # # @api private # def node s(type, *body.map(&:node)) end private # Return positive evaluation # # @param [Object] input # @param [Array] evaluations # # @return [Evaluation] # # @api private # def evaluation_positive(input, evaluations) Evaluation::Nary.success( evaluator: self, input: input, output: true, evaluations: evaluations ) end # Return negative evaluation # # @param [Object] input # @param [Array] evaluations # # @return [Evaluation] # # @api private # def evaluation_negative(input, evaluations) Evaluation::Nary.success( evaluator: self, input: input, output: false, evaluations: evaluations ) end # Return evaluation error # # @param [Object] input # @param [Array] evaluations # # @return [Evaluation] # # @api private # def evaluation_error(input, evaluations) Evaluation::Nary.error( evaluator: self, input: input, evaluations: evaluations ) end # Hook called when module gets included # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do include CONCORD printer(&PRINTER) end end private_class_method :included end # Nary end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/binary.rb0000644000175000017500000000142713715565705022472 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Mixin for binary evaluators module Binary CONCORD = Concord::Public.new(:left, :right) PRINTER = lambda do |_| name indent do visit(:left) visit(:right) end end # Return node # # @return [AST::Node] # # @api private # def node s(type, left.node, right.node) end private # Hook called when module gets included # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do include CONCORD printer(&PRINTER) end end private_class_method :included end # Nary end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/nullary.rb0000644000175000017500000000360213715565705022671 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Mixin to define nullary evaluators module Nullary CONCORD = Concord::Public.new PRINTER = lambda do |_| name end # Instance methods for nullary evaluators module InstanceMethods # Return default successful evaluation # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation(input) evaluation_success(input, call(input)) end # Return node # # @return [AST::Node] # # @api private # def node s(type) end private # Return evaluation error for input # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation_error(input) Evaluation::Nullary.new( evaluator: self, input: input, output: Undefined, success: false ) end # Return evaluation success for input and output # # @param [Object] input # @param [Object] output # # @return [Evaluation] # # @api private # def evaluation_success(input, output) Evaluation::Nullary.new( evaluator: self, input: input, output: output, success: true ) end end # InstanceMethods # Hook called when module gets included # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do include InstanceMethods, CONCORD printer(&PRINTER) end end private_class_method :included end # Nullary end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate.rb0000644000175000017500000000073413715565705023146 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Abstract namespace class for predicate evaluators class Predicate < self include Transformer::Intransitive # Return inverse evaluator # # This is a very naive implementation. # Subclasses can do a more elaborated choice. # # @return [Evaluator] # # @api private # def inverse Negation.new(self) end end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/unary.rb0000644000175000017500000000307713715565705022347 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Mixin for unary evaluators module Unary CONCORD = Concord::Public.new(:operand) PRINTER = lambda do |_| name indent do visit(:operand) end end # Return node # # @return [AST::Node] # # @api private # def node s(type, operand.node) end private # Return success evaluation for input # # @param [Object] input # # @return [Evalation::Unary] # # @api private # def evaluation_success(input, operand_evaluation, output) Evaluation::Unary.success( evaluator: self, input: input, operand_evaluation: operand_evaluation, output: output ) end # Return error evaluation for input # # @param [Object] input # # @return [Evalation::Unary] # # @api private # def evaluation_error(input, operand_evaluation) Evaluation::Unary.error( evaluator: self, input: input, operand_evaluation: operand_evaluation ) end private # Hook called when module gets included # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do include CONCORD printer(&PRINTER) end end private_class_method :included end # Unary end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer.rb0000644000175000017500000000305313715565705023545 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator # Abstract namespace class for transforming evaluators class Transformer < self include AbstractType # Error raised when transformation cannot continue class TransformError < RuntimeError include Concord.new(:transformer, :input) end # Test evaluator transformer is transitive # # A transitive evaluator allows to inverse an operation # via its #inverse evaluator. # # @return [true] # if transformer is transitive # # @return [false] # otherwise # # @api private # abstract_method :transitive? # Mixin for evaluators that are transitive by definition module Transitive # Test if evaluator is transitive # # @return [false] # # @api private # def transitive? true end end # Intransitive # Mixin for evaluators that are intransitive by definition module Intransitive # Test if evaluator is transitive # # @return [false] # # @api private # def transitive? false end end # Intransitive private # Raise transform error # # @param [Object] input # # @raise [TransformError] # # @return [undefined] # # @api private # def raise_transform_error(input) fail TransformError.new(self, input) end end # Transform end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/nullary/0000755000175000017500000000000013715565705022343 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluator/nullary/parameterized.rb0000644000175000017500000000203013715565705025517 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator module Nullary # Mixin to define parameterized nullary evaluators module Parameterized CONCORD = Concord::Public.new(:param) PRINTER = lambda do |_| name indent do attribute :param end end # Mixin for nullary parameterized evaluators module InstanceMethods # Return node # # @return [AST::Node] # # @api private # def node s(type, param) end end # InstanceMethods # Hook called when module gets included # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do include InstanceMethods, Nullary::InstanceMethods, CONCORD printer(&PRINTER) end end private_class_method :included end # Nullary end # Parameterized end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/0000755000175000017500000000000013715565705022615 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluator/predicate/negation.rb0000644000175000017500000000203213715565705024743 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Predicate negation class Negation < self include Unary register :negate # Return evaluation for input # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation(input) operand_output = operand.call(input) evaluation_success(input, operand_output, !operand_output) end # Call evaluator # # @param [Object] input # # @return [true] # if input NOT evaluated to true under operand # # @return [false] # otherwise # # @api private # def call(input) !operand.call(input) end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse operand end end # Negation end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/primitive.rb0000644000175000017500000000211113715565705025145 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Abstract namespace class for predicate evaluators on primitives class Primitive < self include Nullary::Parameterized # Evaluator for exact primitive match class Exact < self register :primitive # Call evaluator # # @param [Object] object # # @return [true] # if object's type is #equal? # # @api private # def call(object) object.class.equal?(param) end end # Exact # Evaluator for permissive primtivie match class Permissive < self register :is_a # Call evaluator # # @param [Object] object # # @return [true] # if objects type equals exactly # # @api private # def call(object) object.kind_of?(param) end end # Permissive end # Primitive end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/boolean.rb0000644000175000017500000000374513715565705024572 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Evaluator for nary boolean predicates class Boolean < self include Nary # Call evaluator with input # # @param [Object] input # # @return [Boolean] # # @api private # def call(input) body .public_send( self.class::ENUMERABLE_METHOD ) { |evaluator| evaluator.call(input) } end # Return evaluation for input # # @param [Object] input # # @return [Evaluation::Nary] # # @api private # def evaluation(input) klass = self.class evaluations = body.each_with_object([]) do |evaluator, aggregate| evaluation = evaluator.evaluation(input) aggregate << evaluation next if evaluation.output.equal?(klass::OUTPUT_EXPECTATION) return send(klass::ERROR_METHOD, input, aggregate) end send(klass::SUCCESS_METHOD, input, evaluations) end # Evaluator for nary and predicates class And < self register :and ENUMERABLE_METHOD = :all? OUTPUT_EXPECTATION = true ERROR_METHOD = :evaluation_negative SUCCESS_METHOD = :evaluation_positive end # And # Evaluator for nary or predicates class Or < self register :or ENUMERABLE_METHOD = :any? OUTPUT_EXPECTATION = false ERROR_METHOD = :evaluation_positive SUCCESS_METHOD = :evaluation_negative end # Or # Evaluator for nary xor predicates class Xor < self register :xor ENUMERABLE_METHOD = :one? OUTPUT_EXPECTATION = false ERROR_METHOD = :evaluation_positive SUCCESS_METHOD = :evaluation_negative end # Xor end # Boolean end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/eql.rb0000644000175000017500000000217713715565705023732 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Binary equal evaluator class EQL < self include Binary register :eql # Call evaluator # # @param [Object] input # # @return [true] # if input is semantically equivalent to expectation # # @return [false] # otherwise # # @api private # def call(input) left.call(input).eql?(right.call(input)) end # Return evaluation # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation(input) left_evaluation = left.evaluation(input) right_evaluation = right.evaluation(input) Evaluation::Binary.success( evaluator: self, input: input, output: left_evaluation.output.eql?(right_evaluation.output), left_evaluation: left_evaluation, right_evaluation: right_evaluation ) end end # EQL end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/tautology.rb0000644000175000017500000000116313715565705025172 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Evaluator for trautology class Tautology < self include Nullary register :true # Call predicate evaluator # # @param [Object] _input # # @return [true] # # @api private # def call(_input) true end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse Contradiction.new end end # Tautology end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/predicate/contradiction.rb0000644000175000017500000000117513715565705026006 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Predicate # Evaluator for contradiction class Contradiction < self include Nullary register :false # Call predicate evaluator # # @param [Object] _input # # @return [false] # # @api private # def call(_input) false end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse Tautology.new end end # Contradiction end # Predicate end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/0000755000175000017500000000000013715565705023217 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluator/transformer/attribute.rb0000644000175000017500000000101213715565705025541 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Transformer to return a specific attribute of input class Attribute < self include Nullary::Parameterized, Intransitive register :attribute # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) input.public_send(param) end end # Attribute end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/input.rb0000644000175000017500000000127013715565705024703 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Identity transformer which always returns +input+ class Input < self include Nullary, Transitive register :input # Call evaluator with input # # @param [Object] input # # @return [Object] # always returns input # # @api private # def call(input) input end # Return inverse evaluator # # @return [Evaluator::Transformer] # # @api private # def inverse self end end # Input end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/guard.rb0000644000175000017500000000251113715565705024645 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Transformer that allows to guard transformation process # with a predicate on input class Guard < self include Unary, Transitive register :guard # Call evaluator # # @param [Object] input # # @return [Object] # if input evaluates true under predicate # # @raise [TransformError] # otherwise # # @api private # def call(input) if operand.call(input) input else fail TransformError.new(self, input) end end # Return evaluation # # @param [Object] input # # @return [Evaluation::Guard] # # @api private # def evaluation(input) operand_evaluation = operand.evaluation(input) if operand_evaluation.output evaluation_success(input, operand_evaluation, input) else evaluation_error(input, operand_evaluation) end end # Return inverse evaluator # # @return [self] # # @api private # def inverse self end end # Guard end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/block.rb0000644000175000017500000000344113715565705024640 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Evaluator to perform n transformations in a row class Block < self include Nary register :block # Test if evaluator is transitive # # @return [true] # if block is transitive # # @return [false] # otherwise # # @api private # def transitive? body.all?(&:transitive?) end # Call transformer # # @param [Object] input # # @return [Object] # # @api private # def call(input) body.reduce(input) do |state, evaluator| evaluator.call(state) end end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse self.class.new(body.reverse.map(&:inverse)) end # Return evaluation for input # # @param [Object] input # # @return [Evaluation::Nary] # # @api private # # rubocop:disable MethodLength # def evaluation(input) state = input evaluations = body.each_with_object([]) do |evaluator, aggregate| evaluation = evaluator.evaluation(state) aggregate << evaluation unless evaluation.success? return evaluation_error(input, aggregate) end state = evaluation.output end Evaluation::Nary.success( evaluator: self, input: input, output: state, evaluations: evaluations ) end end # Block end # Transformer end # Evaluato end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/domain.rb0000644000175000017500000000335513715565705025021 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Abstract namespace class for transformers from/to domain class Domain < self include AbstractType, Nullary::Parameterized, Transitive private # Call block with param attributes # # @param [Object] aggregate # # @return [Object] # # @api private # def transform(aggregate, &block) param.attributes.each_with_object(aggregate, &block) end # Mixin for domain dumpers module Dump # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse self.class::Load.new(param) end private # Dump object # # @param [Symbol] left # @param [Symbol] right # # @return [Object] # # @api private # def dump(&block) transform({}, &block) end end # Dump # Mixin for domain loaders module Load include AbstractType # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse self.class::Dump.new(param) end private # Load object # # @param [Symbol] left # @param [Symbol] right # # @return [Object] # # @api private # def load(&block) transform(param.model.allocate, &block) end end # Load end # Domain end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/static.rb0000644000175000017500000000104113715565705025027 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Transformer that always returns the passed +param+ class Static < self include Nullary::Parameterized, Intransitive register :static # Call evaluator with input # # @param [Object] _input # # @return [Object] # alwasys returns the param # # @api private # def call(_input) param end end # Static end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/hash_transform.rb0000644000175000017500000000667013715565705026573 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Too complex hash transformation evaluator # # FIXME: Should be broken up in better # primitives a decompose, compose pair # # @api private # class HashTransform < self include Nary register :hash_transform # Test if evaluator is transitive # # FIXME: Needs to be calculated dynamically # # @return [true] # if evaluator is transitive # # @return [false] # otherwise # # @api private # def transitive? body.all?(&self.class.method(:transitive_keypair?)) end # Test if evaluator is a keypair # # FIXME: Refactor the need for this away. # # This is a side effect from this class is # generally to big in sense of SRP. # Must be refactorable away. But dunno now. # Still exploring. # # @param [Evaluator] # # @api private # def self.transitive_keypair?(evaluator) return false unless evaluator.kind_of?(Block) body = evaluator.body left, operator, right = body if body.length.equal?(3) left.kind_of?(Key::Fetch) && right.kind_of?(Key::Dump) && operator.transitive? end # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) content = body.map do |node| node.call(input) end Hash[content] end # Return inverse evaluator # # @return [HashTransform] # # @api private # def inverse self.class.new(body.map(&:inverse)) end # Return evaluation # # @param [Input] # # @return [Evaluation::Nary] # # @api private # # rubocop:disable MethodLength # def evaluation(input) evaluations = body.each_with_object([]) do |evaluator, aggregate| evaluation = evaluator.evaluation(input) aggregate << evaluation unless evaluation.success? return evaluation_error(input, aggregate) end end output = Hash[evaluations.map(&:output)] Evaluation::Nary.success( evaluator: self, input: input, evaluations: evaluations, output: output ) end private # Return evaluation error # # @return [Object] input # # @return [Array] evaluations # # @api private # def evaluation_error(input, evaluations) Evaluation::Nary.error( evaluator: self, input: input, evaluations: evaluations ) end # Build evaluator from node # # @param [Compiler] compiler # @param [Node] node # # @return [Evaluator] # # @api private # def self.build(compiler, node) body = node.children.map do |child| compiler.call(child) end new(body) end end # HashTransform end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/coerce.rb0000644000175000017500000000722613715565705025013 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Abstract namespace class for coercing transformers class Coerce < self include AbstractType, Nullary::Parameterized, Transitive # Parse mixin for cases the parsing possibly results # in Argument and Type errors. module Parse # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) invoke(input) rescue ArgumentError, TypeError raise_transform_error(input) end # Return evaluation # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation(input) evaluation_success(input, invoke(input)) rescue ArgumentError, TypeError evaluation_error(input) end end # Parse # Evaluator for parsing an integer class ParseInt < self include Parse register :parse_int # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse IntToString.new(param) end private # Invoke coercion # # @return [Integer] # # @raise [ArgumentError, TypeError] # if coercion does not succeed # # @api private # def invoke(input) Integer(input, param) end end # ParseInt # Evaluator for dumping fixnums to strings class IntToString < self register :int_to_string # Call evaluator # # @param [Object] input # # @return [Hash] # # @api private # def call(input) input.to_s(param) end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse ParseInt.new(param) end end # IntToString # Evaluator for parsing an ISO8601 String into a DateTime class ParseIso8601DateTime < self include Parse register :parse_iso8601_date_time # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse DateTimeToIso8601String.new(param) end private # Invoke coercion # # @return [DateTime] # # @raise [ArgumentError, TypeError] # if coercion does not succeed # # @api private # def invoke(input) DateTime.iso8601(input) end end # ParseIso8601DateTime # Evaluator for dumping a DateTime to an ISO8601 string class DateTimeToIso8601String < self register :date_time_to_iso8601_string # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) input.iso8601(param) end # Return inverse evaluator # # @return [Evaluator] # # @api private # def inverse ParseIso8601DateTime.new(param) end end # DateTimeToIso8601String end # Fixnum end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/domain/0000755000175000017500000000000013715565705024466 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/evaluator/transformer/domain/param.rb0000644000175000017500000000230313715565705026111 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer class Domain < self # Domain specific transformer parameter class Param include Adamantium, Concord::Public.new(:model, :attribute_names) # Return attributes # # @return [Enumerable] # # @api private # def attributes attribute_names.map(&Attribute.method(:new)) end memoize :attributes # Attribute on a domain transformer param class Attribute include Adamantium, Concord::Public.new(:name) # Return instance variable name # # @return [Symbol] # # @api private # def ivar_name :"@#{name}" end memoize :ivar_name # Return writer name # # @return [Symbol] # # @api private # def writer :"#{name}=" end memoize :writer end # Attribute end # Param end # Domain end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/domain/attribute_accessors.rb0000644000175000017500000000300413715565705031060 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer class Domain # Abstract namespace class for domain objects via attribute accessors class AttributeAccessors < self include AbstractType # Evaluator for dumping domain objects via instance variables class Dump < self include Domain::Dump register :dump_attribute_accessors # Call evaluator # # @param [Object] input # # @return [Hash] # # @api private # def call(input) dump do |attribute, attributes| name = attribute.name attributes[name] = input.public_send(name) end end end # Dump # Evaluator for loading domain objects via attributes hash class Load < self include Domain::Load register :load_attribute_accessors # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) load do |attribute, object| object.public_send( attribute.writer, input.fetch(attribute.name) ) end end end # Load end # Domain end # Anima end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/domain/attribute_hash.rb0000644000175000017500000000231513715565705030022 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer class Domain < self # Abstract namespace class for domain objects on attributes hash class AttributeHash < self include AbstractType # Evaluator for dumping domain objects via attributes hash class Dump < self include Domain::Dump register :dump_attribute_hash # Call evaluator # # @param [Object] input # # @return [Hash] # # @api private # def call(input) input.to_h end end # Dump # Evaluator for loading domain objects via attributes hash class Load < self include Domain::Load register :load_attribute_hash # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) param.model.new(input) end end # Load end # Domain end # Anima end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/domain/instance_variables.rb0000644000175000017500000000304613715565705030652 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer class Domain < self # Abstract namespace class for domain objects via instance variables class InstanceVariables < self include AbstractType # Evaluator for dumping domain objects via instance variables class Dump < self include Domain::Dump register :dump_instance_variables # Call evaluator # # @param [Object] input # # @return [Hash] # # @api private # def call(input) dump do |attribute, attributes| attributes[attribute.name] = input.instance_variable_get(attribute.ivar_name) end end end # Dump # Evaluator for loading domain objects via instance variables class Load < self include Domain::Load register :load_instance_variables # Call evaluator # # @param [Object] input # # @return [Object] # # @api private # def call(input) load do |attribute, object| object.instance_variable_set( attribute.ivar_name, input.fetch(attribute.name) ) end end end # Load end # Domain end # Anima end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/key.rb0000644000175000017500000000344013715565705024335 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Abstract namespace class for evaluators operating on hash keys class Key < self include AbstractType, Nullary::Parameterized, Intransitive # Evaluator for dumping hash keys class Dump < self register :key_dump # Call evaluator # # @param [Object] object # # @return [Array] # # @api private # def call(object) [param, object] end # Return inverse evaluator # # @return [Fetch] # # @api private # def inverse Fetch.new(param) end end # Dump # Evaluator to fetch a specific hash key class Fetch < self register :key_fetch # Call evaluator # # @param [Hash] object # # @return [Object] # # @api private # def call(object) object.fetch(param) do fail TransformError.new(self, object) end end # Return evaluation # # @param [Object] input # # @return [Evaluation] # # @api private # def evaluation(input) output = input.fetch(param) do return evaluation_error(input) end evaluation_success(input, output) end # Return inverse evaluator # # @return [Dump] # # @api private # def inverse Dump.new(param) end end # Fetch end # Key end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/custom.rb0000644000175000017500000000133413715565705025057 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer < self # Custom transformer with external injected behavior class Custom < self include Nullary::Parameterized, Transitive register :custom # Call transformer with input # # @param [Object] # # @return [undefinedo] # # @api private # def call(input) param.first.call(input) end # Return inverse transformer # # @return [Evaluator::Transformer] # # @api private # def inverse self.class.new(param.reverse) end end # Custom end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/merge.rb0000644000175000017500000000077613715565705024655 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Transformer to merge input into defaults class Merge < self include Intransitive, Nullary::Parameterized register :merge # Call evaluator for input # # @param [Object] input # # @return [Object] output # # @api private # def call(input) param.merge(input) end end # Merge end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/evaluator/transformer/map.rb0000644000175000017500000000433213715565705024323 0ustar debbiecocoadebbiecocoamodule Morpher class Evaluator class Transformer # Transformer over each element in an enumerable class Map < self include Unary register :map # Test if evaluator is transitive # # @return [true] # if evaluator is transitive # # @return [false] # otherwise # # @api private # def transitive? operand.transitive? end # Call evaluator # # @param [Enumerable#map] input # # @return [Enumerable] # if input evaluates true under predicate # # @raise [TransformError] # otherwise # # @api private # def call(input) input.map(&operand.method(:call)) end # Return evaluation # # @param [Enumerable#map] input # # @return [Evaluation] # # @api private # # rubocop:disable MethodLength # def evaluation(input) evaluations = input.each_with_object([]) do |item, aggregate| evaluation = operand.evaluation(item) aggregate << evaluation unless evaluation.success? return evaluation_error(input, aggregate) end end Evaluation::Nary.success( evaluator: self, input: input, output: evaluations.map(&:output), evaluations: evaluations ) end # Return inverse evaluator # # @return [Evaluator::Transformer] # # @api private # def inverse self.class.new(operand.inverse) end private # Return evaluation error # # @param [Object] input # @param [Array] evaluations # # @return [Evaluation] # # @api private # def evaluation_error(input, evaluations) Evaluation::Nary.error( evaluator: self, input: input, evaluations: evaluations ) end end # Guard end # Transformer end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/type_lookup.rb0000644000175000017500000000176313715565705021561 0ustar debbiecocoadebbiecocoamodule Morpher # Type lookup via registry and superclass chaining # # TODO: Cache results. # class TypeLookup include Adamantium::Flat, Concord.new(:registry) # Error raised on compiling unknown nodes class TypeNotFoundError < RuntimeError include Concord.new(:type) # Return exception error message # # @return [String] # # @api private # def message "Node type: #{type.inspect} is unknown" end end # TypeNotFoundError # Perform type lookup # # @param [Object] object # # @return [Object] # if found # # @raise [TypeNotFoundError] # otherwise # # @api private # def call(object) current = target = object.class while current != Object if registry.key?(current) return registry.fetch(current) end current = current.superclass end fail TypeNotFoundError, target end end # TypeLookup end # Morpher morpher-0.2.6/lib/morpher/node_helpers.rb0000644000175000017500000000046013715565705021647 0ustar debbiecocoadebbiecocoamodule Morpher # Node helpers module NodeHelpers # Build node # # @param [Symbol] type # # @return [Parser::AST::Node] # # @api private # def s(type, *children) AST::Node.new(type, children) end module_function :s end # NodeHelpers end # Morpher morpher-0.2.6/lib/morpher/compiler/0000755000175000017500000000000013715565705020465 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/compiler/emitter.rb0000644000175000017500000000325213715565705022465 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler # Abstract target indepentand emitter class Emitter include AbstractType, Adamantium::Flat, NodeHelpers, Procto.call(:output) extend NodeHelpers # Return output of emitter # # @return [Object] # # @api private # abstract_method :output # Return node # # @return [Node] # # @api private # abstract_method :node private :node private # Return children # # @return [Array] # # @api private # def children node.children end # Assert number of child nodes # # @return [self] # if assertion is fullfilled # # @raise [NodeError] # otherwise # # @api private # def assert_children_amount(expected_amount) actual_amount = children.length fail Error::NodeChildren.new(node, expected_amount) unless actual_amount.equal?(expected_amount) end # Name children # # @return [undefined] # # @api private # # rubocop:disable MethodLength # def self.children(*names) names.each_with_index do |name, index| define_method(name) do children.at(index) end private name end define_method(:named_children) do names end private :named_children define_method(:remaining_children) do children.drop(names.length) end private :remaining_children end private_class_method :children end # Emitter end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/evaluator/0000755000175000017500000000000013715565705022467 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/compiler/evaluator/emitter.rb0000644000175000017500000001130313715565705024463 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Evaluator # Emitter for evaluators class Emitter < Compiler::Emitter include Registry, Concord.new(:compiler, :evaluator_klass, :node) # Return output # # @return [Evaluator] # # @api private # def output validate_node evaluator end memoize :output # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # abstract_method :validate_node private :validate_node # Return evaluator # # @return [Evaluator] # # @api private # abstract_method :evaluator private :evaluator # Emitter for nullary non parameterized evaluators class Nullary < self register Morpher::Evaluator::Nullary private # Return output # # @return [Evaluator] # # @api private # def evaluator evaluator_klass.new end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node assert_children_amount(0) end # Emitter for nullary parameterized evaluators class Parameterized < self register Morpher::Evaluator::Nullary::Parameterized children :param private # Return output # # @return [Evaluator] # # @api private # def evaluator evaluator_klass.new(effective_param) end # Return effective param # # @return [Object] # # @api private # def effective_param if param.kind_of?(AST::Node) && param.type.equal?(:raw) && param.children.length.equal?(1) param.children.first else param end end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node assert_children_amount(1) end end # Paramterized end # Nullary # Emitter for unary evaluators class Unary < self register Morpher::Evaluator::Unary children :operand private # Return evaluator # # @return [Evaluator] # # @api private # def evaluator evaluator_klass.new(compiler.call(operand)) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node assert_children_amount(1) end end # Unary # Emitter for unary evaluators class Binary < self register Morpher::Evaluator::Binary children :left, :right private # Return evaluator # # @return [Evaluator] # # @api private # def evaluator evaluator_klass.new( compiler.call(left), compiler.call(right) ) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node assert_children_amount(2) end end # Unary # Emitter for nary evaluators class Nary < self register Morpher::Evaluator::Nary private # Return evaluator # # @return [Evaluator] # # @api private # def evaluator evaluator_klass.new(children.map(&compiler.method(:call))) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node end end # Nary end # Emitter end # Evaluator end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/error.rb0000644000175000017500000000326013715565705022144 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler # Abstract error class for compiler errors class Error < RuntimeError include AbstractType # Error raised when node children have incorrect amount class NodeChildren < self include Concord.new(:node, :expected_amount) # Return exception message # # @return [String] # # @api private # def message "Expected #{expected_amount} #{_children} for #{type}, got #{actual_amount}: #{children}" end private # Return inspected type # # @return [String] # # @api private # def type node.type.inspect end # Return actual amount of children # # @return [String] # # @api private # def actual_amount children.length end # Return children # # @return [Array] # # @api private # def children node.children end # Return user firendly children message # # @return [String] # # @api private # def _children expected_amount.equal?(1) ? 'child' : 'children' end end # NodeChildren # Error raised on compiling unknown nodes class UnknownNode < self include Concord.new(:type) # Return exception error message # # @return [String] # # @api private # def message "Node type: #{type.inspect} is unknown" end end # UnknownNode end # Error end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/0000755000175000017500000000000013715565705023213 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/compiler/preprocessor/emitter.rb0000644000175000017500000000204313715565705025210 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor # Abstract preprocessor emitter class Emitter < Compiler::Emitter include Registry, Concord.new(:preprocessor, :node) # Return output # # @return [AST::Node] # # @api private # def output validate_node processed_node end memoize :output private # Visit node # # @param [Node] node # original untransformed node # # @return [Node] # transformed node # # @api private # def visit(node) preprocessor.call(node) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node assert_children_amount(named_children.length) end end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/0000755000175000017500000000000013715565705024664 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/param.rb0000644000175000017500000000207213715565705026312 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor class Emitter # Param domain transformer specific emitter class Param < self register :param children :model private # Return output # # @return [Node] # # @api private # def processed_node param = Morpher::Evaluator::Transformer::Domain::Param.new( model, remaining_children ) s(:raw, param) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node remaining_children.each_with_index do |child, index| next if child.kind_of?(Symbol) fail Error::ChildType, Symbol, child, index end end end # Noop end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/noop.rb0000644000175000017500000000163713715565705026173 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor class Emitter # Noop emitter just descending into children class Noop < self private # Return output # # @return [Node] # # @api private # def processed_node mapped_children = node.children.map do |child| if child.kind_of?(node.class) visit(child) else child end end s(node.type, *mapped_children) end # Validate node # # @return [undefined] # if successful # # @raise [Error] # otherwise # # @api private # def validate_node end end # Noop end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/anima.rb0000644000175000017500000000256613715565705026307 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor class Emitter # Abstract base class for anima emitters class Anima < self include AbstractType children :model private # Return domain param # # @return [Transformer::Domain::Param] # # @api private # def param Morpher::Evaluator::Transformer::Domain::Param.new( model, model.anima.attribute_names ) end class Dump < self register :anima_dump private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node s(:dump_attribute_hash, param) end end # Dump class Load < self register :anima_load private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node s(:load_attribute_hash, param) end end # Load end # Anima end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/boolean.rb0000644000175000017500000000115213715565705026627 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor class Emitter # Preprocessor for boolean primitive class Boolean < self register :boolean children NODE = s(:xor, s(:primitive, TrueClass), s(:primitive, FalseClass)) private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node NODE end end # Boolean end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor/emitter/key.rb0000644000175000017500000000343013715565705026001 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler class Preprocessor class Emitter # Namespace class for key preprocessors class Key < self include AbstractType # Key symbolization preprocessor class Symbolize < self register :key_symbolize children :key, :operand private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node s(:key_transform, key.to_s, key.to_sym, operand) end end # Symbolize # Neutral key preprocessor class Neutral < self register :key children :key, :operand private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node s(:key_transform, key, key, operand) end end # Neutral # Key transformation preprocessor class Transform < self register :key_transform children :from, :to, :operand private # Return transformed node # # @param [Node] node # # @return [Node] # # @api private # def processed_node s( :block, s(:key_fetch, from), visit(operand), s(:key_dump, to) ) end end # Transform end # Key end # Emitter end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/evaluator.rb0000644000175000017500000000244213715565705023016 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler # Compiler with evaluators as output class Evaluator < self include Concord.new(:evaluators, :emitters) # Return evaluator tree for node # # @param [Node] node # # @return [Evalautor] # on success # # @raise [Compiler::Error] # on error # # @api private # def call(node) evaluator = evaluator(node) emitter = emitter(evaluator) emitter.call(self, evaluator, node) end private # Lookup evaluator for node # # @param [Node] # # @return [Class:Evaluator] # if found # # @raise [Error::UnknownNode] # otherwise # # @api private # def evaluator(node) type = node.type evaluators.fetch(type) do fail Error::UnknownNode, type end end # Return emitter for evaluator # # @param [Class:Evalautor] # # @return [#call] # # @api private # def emitter(evaluator) emitters.each do |arity, emitter| return emitter if evaluator.ancestors.include?(arity) end fail Error::UnknownNode, evaluator end end # Evaluator end # Compiler end # Morpher morpher-0.2.6/lib/morpher/compiler/preprocessor.rb0000644000175000017500000000113413715565705023537 0ustar debbiecocoadebbiecocoamodule Morpher class Compiler # AST preprocessor class Preprocessor < self include Concord.new(:emitters) # Call preprocessor # # @param [Node] node # the raw AST node after DSL # # @return [Node] # the transformed ast node # # @api private # def call(node) loop do emitter = emitters.fetch(node.type, Emitter::Noop) node = emitter.call(self, node) break if emitter.equal?(Emitter::Noop) end node end end # Preprocessor end # Compiler end # Morpher morpher-0.2.6/lib/morpher/printer.rb0000644000175000017500000001044013715565705020662 0ustar debbiecocoadebbiecocoamodule Morpher # Evaluation and Evaluator pretty printer class Printer include Adamantium::Flat, Concord.new(:object, :output, :indent_level) INDENT = ' '.freeze REGISTRY = {} # Run pretty printer on object # # @param [Object] object # the object to be pretty printed # @param [IO] output # the output to write to # @param [Fixnum] indent_level # the current indentation level # # @return [self] # # @api private # def self.run(object, output, indent_level = 0) printer = new(object, output, indent_level) block = lookup(object) printer.instance_eval(&block) end # Perform type lookup # # FIXME: Instanciate type lookup once and allow caching. # # @param [Evaluation, Evaluator] object # # @return [Proc] # if found # # @raise [PrinterMissingException] # otherwise # # @api private # def self.lookup(object) TypeLookup.new(REGISTRY).call(object) end private # Visit a child # # @param [Node] child # # @api private # # @return [undefined] # def visit_child(child) self.class.run(child, output, indent_level.succ) end # Visit a child by name # # @param [Symbol] name # the attribute name of the child to visit # # @return [undefined] # # @api private # def visit(name) child = object.public_send(name) child_label(name) visit_child(child) end # Visit many children # # @param [Symbol] name # the name of the collection attribute with children to visit # # @return [undefined] # # @api private # def visit_many(name) children = object.public_send(name) child_label(name) children.each do |child| visit_child(child) end end # Print attribute class # # @param [Symbol] name # # @return [undefined] # # @api private # def attribute_class(name) label_value(name, object.public_send(name).class) end # Print inspected attribute value with label # # @return [undefined] # # @api private # def attribute(name) label_value(name, object.public_send(name)) end # Print attributes of object # # @return [undefined] # # @api private # def attributes(*names) names.each do |name| attribute(name) end end # Print name of object # # @return [undefined] # # @api private # def name puts(object.class.name) end # Return string indented with current level # # @param [String] content # # @return [String] # # @api private # def indented(content) "#{indentation_prefix}#{content}" end # Return indentation prefix # # @return [String] # # @api private # def indentation_prefix INDENT * indent_level end memoize :indentation_prefix # Add content to output at current indentation and close line # # @param [String] content # # @return [undefined] # # @api private # def puts(string) output.puts(indented(string)) end # Write content to output at current indentation # # @param [String] content # # @return [undefined] # # @api private # def write(string) output.write(indented(string)) end # Write child label to output at current indentation # # @param [String] label # # @return [undefined] # # @api private # def child_label(string) puts("#{string}:") end # Call block inside indented context # # @return [undefined] # # @api private # def indent(&block) printer = new(object, output, indent_level.succ) printer.instance_eval(&block) end # Return new printer # # @return [Printer] # # @api private # def new(*arguments) self.class.new(*arguments) end # Print label with value # # @param [String] label # @param [Object] value # # @return [undefined] # # @api private # def label_value(label, value) write("#{label}: ") output.puts(value.inspect) end end # Printer end # Morpher morpher-0.2.6/lib/morpher/evaluator.rb0000644000175000017500000000130613715565705021202 0ustar debbiecocoadebbiecocoamodule Morpher # Abstract namespace class for non tracking evaluators class Evaluator include Adamantium::Flat, Registry, AbstractType, Printer::Mixin, NodeHelpers # Call evaluator in non tracking mode # # @param [Object] input # # @return [Object] # # @api private # abstract_method :call # Call evaluator in tracking mode # # @param [Object] input # # @return [Evaluation] # # @api private # abstract_method :evaluation # Return inverse evaluator # # @return [Evaluator] # # @api private # abstract_method :inverse end # Evaluator end # Morpher morpher-0.2.6/lib/morpher/printer/0000755000175000017500000000000013715565705020336 5ustar debbiecocoadebbiecocoamorpher-0.2.6/lib/morpher/printer/mixin.rb0000644000175000017500000000214413715565705022010 0ustar debbiecocoadebbiecocoamodule Morpher class Printer # Printer behavior mixin module Mixin # Class level methods to be mixed in module ClassMethods # Register printer block for class # # @return [self] # # @api private # def printer(&block) REGISTRY[self] = block end end # ClassMethods # Instance level methods to be mixed in module InstanceMethods # Return description # # @return [String] # # @api private # def description io = StringIO.new Printer.run(self, io) io.rewind io.read end end # InstanceMethods # Callback whem module gets included # # @param [Class, Module] descendant # # @return [undefined] # # @api private # def self.included(descendant) descendant.class_eval do extend ClassMethods include InstanceMethods end end private_class_method :included end # Mixin end # Printer end # Morpher morpher-0.2.6/lib/morpher.rb0000644000175000017500000000621713715565705017206 0ustar debbiecocoadebbiecocoarequire 'abstract_type' require 'concord' require 'anima' require 'ast' require 'procto' # Library namespace module module Morpher Undefined = Module.new.freeze # Return evaluator from node # # @param [Node] # # @return [Evaluator] # # @api private # def self.compile(node) node = Compiler::Preprocessor::DEFAULT.call(node) Compiler::Evaluator::DEFAULT.call(node) end # Return evaluate block to produce an AST node # # @return [AST::Node] # # @api private # def self.sexp(&block) NodeHelpers.module_eval(&block) end # Build morpher from yielding sexp blog # # @return [Evaluator] # # @api private # def self.build(&block) compile(sexp(&block)) end end # Morpher require 'morpher/node_helpers' require 'morpher/registry' require 'morpher/printer' require 'morpher/printer/mixin' require 'morpher/evaluator' require 'morpher/evaluator/nullary' require 'morpher/evaluator/nullary/parameterized' require 'morpher/evaluator/unary' require 'morpher/evaluator/binary' require 'morpher/evaluator/nary' require 'morpher/evaluator/transformer' require 'morpher/evaluator/transformer/block' require 'morpher/evaluator/transformer/key' require 'morpher/evaluator/transformer/guard' require 'morpher/evaluator/transformer/attribute' require 'morpher/evaluator/transformer/hash_transform' require 'morpher/evaluator/transformer/map' require 'morpher/evaluator/transformer/static' require 'morpher/evaluator/transformer/input' require 'morpher/evaluator/transformer/merge' require 'morpher/evaluator/transformer/coerce' require 'morpher/evaluator/transformer/custom' require 'morpher/evaluator/transformer/domain' require 'morpher/evaluator/transformer/domain/param' require 'morpher/evaluator/transformer/domain/attribute_hash' require 'morpher/evaluator/transformer/domain/instance_variables' require 'morpher/evaluator/transformer/domain/attribute_accessors' require 'morpher/evaluator/predicate' require 'morpher/evaluator/predicate/eql' require 'morpher/evaluator/predicate/primitive' require 'morpher/evaluator/predicate/negation' require 'morpher/evaluator/predicate/tautology' require 'morpher/evaluator/predicate/contradiction' require 'morpher/evaluator/predicate/boolean' require 'morpher/evaluation' require 'morpher/evaluation' require 'morpher/type_lookup' require 'morpher/compiler' require 'morpher/compiler/error' require 'morpher/compiler/emitter' require 'morpher/compiler/evaluator' require 'morpher/compiler/evaluator/emitter' require 'morpher/compiler/preprocessor' require 'morpher/compiler/preprocessor/emitter' require 'morpher/compiler/preprocessor/emitter/noop' require 'morpher/compiler/preprocessor/emitter/key' require 'morpher/compiler/preprocessor/emitter/param' require 'morpher/compiler/preprocessor/emitter/boolean' require 'morpher/compiler/preprocessor/emitter/anima' module Morpher class Compiler class Preprocessor # Default preprocessor compiler DEFAULT = new(Emitter::REGISTRY.freeze) end # Preprocessor class Evaluator # Default evaluator compiler DEFAULT = new(Morpher::Evaluator::REGISTRY, Emitter::REGISTRY.freeze) end # Evaluator end # Compiler end # Morpher morpher-0.2.6/Gemfile0000644000175000017500000000004713715565705015725 0ustar debbiecocoadebbiecocoasource 'https://rubygems.org' gemspec morpher-0.2.6/Rakefile0000644000175000017500000000520213715565705016075 0ustar debbiecocoadebbiecocoarequire 'devtools' Devtools.init_rake_tasks Rake.application.load_imports task('metrics:mutant').clear namespace :metrics do task mutant: :coverage do success = Kernel.system(*%w[ bundle exec mutant --zombie --use rspec --include lib --require morpher --since HEAD~1 -- Morpher* ]) or fail 'Mutant task is not successful' end end # NOTICE: This uses private interface of morpher that can change at any time. # Its just a placeholder for a better reflection technique!!! namespace :morpher do desc 'List morpher nodes' task :list do class Presenter class Evaluator < self include Concord::Public.new(:name, :evaluator) def arity emitter = Morpher::Compiler::Evaluator::DEFAULT.send(:emitter, evaluator) emitter_ns = Morpher::Compiler::Evaluator::Emitter { emitter_ns::Nullary => :nullary, emitter_ns::Nullary::Parameterized => :nullary_param, emitter_ns::Unary => :unary, emitter_ns::Binary => :binary, emitter_ns::Nary => :nary }.fetch(emitter) end def transitivity ancestors = evaluator.ancestors if ancestors.include?(Morpher::Evaluator::Transformer::Transitive) :yes elsif ancestors.include?(Morpher::Evaluator::Transformer::Intransitive) :no else :dynamic end end def role if evaluator.ancestors.include?(Morpher::Evaluator::Transformer) :transforming else :predicate end end end class Preprocessor include Concord::Public.new(:name, :emitter) def children_nodes emitter.allocate.send(:named_children).join(', ') end end end puts 'Evaluators:' EVALUATOR_FIELDS = [:name, :arity, :transitivity, :role] EVALUATOR_FORMAT = '%-24s - %-15s - %-15s - %-20s'.freeze puts EVALUATOR_FORMAT % EVALUATOR_FIELDS Morpher::Evaluator::REGISTRY.each do |name, evaluator| presenter = Presenter::Evaluator.new(name, evaluator) puts EVALUATOR_FORMAT % EVALUATOR_FIELDS.map(&presenter.method(:public_send)) end puts 'Preprocessors:' PREPROCESSOR_FORMAT = '%-20s - %-20s' PREPROCESSOR_FIELDS = [:name, :children_nodes] puts PREPROCESSOR_FORMAT % PREPROCESSOR_FIELDS Morpher::Compiler::Preprocessor::Emitter::REGISTRY.each do |name, emitter| presenter = Presenter::Preprocessor.new(name, emitter) puts PREPROCESSOR_FORMAT % PREPROCESSOR_FIELDS.map(&presenter.method(:public_send)) end end end morpher-0.2.6/Changelog.md0000644000175000017500000000322013715565705016637 0ustar debbiecocoadebbiecocoa# v0.2.3 2014-04-22 [Compare v0.2.2..v0.2.3](https://github.com/mbj/morpher/compare/v0.2.2...v0.2.3) Changes: * Dependency updates. # v0.2.2 2014-04-10 [Compare v0.2.1..v0.2.2](https://github.com/mbj/morpher/compare/v0.2.1...v0.2.2) Changes: * Add Mutant.sexp returning a morpher AST node via evaluating a block with sexp node API available. * Add Mutant.build returning a morpher evaluator via evaluating a block with sexp node API available. * Fix evaluation errors on Transformer::Map node. * Ensure mutant coverage scores on CI # v0.2.1 2014-03-29 [Compare v0.2.0..v0.2.1](https://github.com/mbj/morpher/compare/v0.2.0...v0.2.1) Changes: * Fix warnings on multiple method definition # v0.2.0 2014-03-09 [Compare v0.1.0..v0.2.0](https://github.com/mbj/morpher/compare/v0.1.0...v0.2.0) Changes: * Add param node s(:param, Model, :some, :attributes) to build Transformer::Domain::Param Breaking-Changes: * Rename {load,dump}_attributes_hash to {load,dump}_attribute_hash * Require {load,dump}_attribute_hash param to be an instance of Transformer::Domain::Param # v0.1.0 2014-03-08 [Compare v0.0.1..v0.1.0](https://github.com/mbj/morpher/compare/v0.0.1...v0.1.0) Breaking-Changes: * Renamed `Morpher.evaluator(node)` to `Morpher.compile(node)` * Rename node: `symbolize_key` to `key_symbolize` * Rename node: `anima_load` to `load_attributes_hash` * Rename node: `anima_dump` to `dump_attributes_hash` * The ability to rescue/report anima specific exceptions has been dropped Changes: * Add {dump,load}_{attribute_accessors,instance_variables} as additional strategies to transform from / to domain objects. # v0.0.1 2014-03-02 First public release. morpher-0.2.6/spec/0000755000175000017500000000000013715565705015363 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/0000755000175000017500000000000013715565705016342 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher_spec.rb0000644000175000017500000000203313715565705021353 0ustar debbiecocoadebbiecocoadescribe Morpher do let(:object) { described_class } describe '.sexp' do subject { object.sexp(&block) } context 'with no block given' do let(:block) { nil } it 'raises an exception' do expect { subject }.to raise_error(ArgumentError) end end context 'with block given' do let(:block) do proc do s(:foo) s(:bar) end end it 'allows to use sexp dsl and returns last value' do should == AST::Node.new(:bar) end end end describe '.build' do subject { object.build(&block) } context 'with no block given' do let(:block) { nil } it 'raises an exception' do expect { subject }.to raise_error(ArgumentError) end end context 'with block given' do let(:block) do proc do s(:foo) s(:true) end end it 'allows to use sexp dsl and returns last value compiled' do should eql(Morpher.compile(s(:true))) end end end end morpher-0.2.6/spec/unit/morpher/0000755000175000017500000000000013715565705020016 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/printer_spec.rb0000644000175000017500000000055013715565705023040 0ustar debbiecocoadebbiecocoadescribe Morpher::Printer::Mixin do describe '#description' do let(:object) do Class.new do include Morpher::Printer::Mixin, Adamantium::Flat def self.name 'Foo' end printer do name end end end subject { object.new.description } it { should eql("Foo\n") } end end morpher-0.2.6/spec/unit/morpher/registry_spec.rb0000644000175000017500000000027313715565705023227 0ustar debbiecocoadebbiecocoadescribe Morpher::Registry do specify do klass = Class.new do include Morpher::Registry register :foo end expect(klass::REGISTRY).to eql(foo: klass) end end morpher-0.2.6/spec/unit/morpher/evaluator/0000755000175000017500000000000013715565705022020 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/nullary/0000755000175000017500000000000013715565705023506 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/nullary/parameterized_spec.rb0000644000175000017500000000073013715565705027701 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Nullary::Parameterized do describe '.include' do let(:object) do Class.new(Morpher::Evaluator) do include Morpher::Evaluator::Nullary::Parameterized def self.name 'Foo' end end end let(:instance) do object.new('foo') end it 'sets up printer' do instance.description.should eql(strip(<<-'TEXT')) Foo param: "foo" TEXT end end end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/0000755000175000017500000000000013715565705023760 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/predicate/eql_spec.rb0000644000175000017500000000046713715565705026107 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::EQL do let(:object) { described_class.new(left, right) } let(:left) { Morpher.compile(s(:static, 1.0)) } let(:right) { Morpher.compile(s(:input)) } let(:positive_input) { 1.0 } let(:negative_input) { 1 } include_examples 'predicate evaluator' end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/contrandiction_spec.rb0000644000175000017500000000041113715565705030331 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Contradiction do let(:object) { described_class.new } let(:valid_input) { double('Input') } let(:expected_output) { false } include_examples 'transforming evaluator on valid input' end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/primitive_spec.rb0000644000175000017500000000105313715565705027326 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Primitive::Exact do let(:object) { described_class.new(Morpher::Evaluator) } let(:positive_input) { Morpher::Evaluator.allocate } let(:negative_input) { Morpher::Evaluator::Predicate.allocate } include_examples 'predicate evaluator' end describe Morpher::Evaluator::Predicate::Primitive::Permissive do let(:object) { described_class.new(Morpher::Evaluator) } let(:positive_input) { Morpher::Evaluator::Predicate.allocate } let(:negative_input) { '' } include_examples 'predicate evaluator' end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/boolean/0000755000175000017500000000000013715565705025377 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/predicate/boolean/xor_spec.rb0000644000175000017500000000135713715565705027554 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Boolean::Xor do let(:object) { described_class.new([body_a, body_b]) } let(:body_a) { Morpher.compile(s(:primitive, TrueClass)) } let(:body_b) { Morpher.compile(s(:primitive, FalseClass)) } let(:negative_input) { nil } let(:positive_input) { true } include_examples 'predicate evaluator' context '#evalaution' do context 'on positive input' do subject { object.evaluation(positive_input) } its(:evaluations) { should eql([body_a.evaluation(positive_input)]) } end context 'on negative input' do subject { object.evaluation(negative_input) } its(:evaluations) { should eql(object.body.map { |node| node.evaluation(negative_input) }) } end end end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/boolean/and_spec.rb0000644000175000017500000000060713715565705027503 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Boolean::And do let(:object) { described_class.new([body_a, body_b]) } let(:body_a) { Morpher.compile(s(:primitive, String)) } let(:body_b) { Morpher.compile(s(:eql, s(:attribute, :length), s(:static, 1))) } let(:negative_input) { '' } let(:positive_input) { 'a' } include_examples 'predicate evaluator' end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/boolean/or_spec.rb0000644000175000017500000000136313715565705027361 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Boolean::Or do let(:object) { described_class.new([body_a, body_b]) } let(:body_a) { Morpher.compile(s(:primitive, String)) } let(:body_b) { Morpher.compile(s(:primitive, Symbol)) } let(:negative_input) { Object.new } let(:positive_input) { '' } include_examples 'predicate evaluator' context '#evalaution' do context 'on positive input' do subject { object.evaluation(positive_input) } its(:evaluations) { should eql([body_a.evaluation(positive_input)]) } end context 'on negative input' do subject { object.evaluation(negative_input) } its(:evaluations) { should eql(object.body.map { |node| node.evaluation(negative_input) }) } end end end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/tautology_spec.rb0000644000175000017500000000040513715565705027345 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Tautology do let(:object) { described_class.new } let(:valid_input) { double('Input') } let(:expected_output) { true } include_examples 'transforming evaluator on valid input' end morpher-0.2.6/spec/unit/morpher/evaluator/predicate/negation_spec.rb0000644000175000017500000000040513715565705027122 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Predicate::Negation do let(:object) { described_class.new(operand) } let(:operand) { Morpher.compile(s(:input)) } let(:positive_input) { false } let(:negative_input) { true } include_examples 'predicate evaluator' end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/0000755000175000017500000000000013715565705024362 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/transformer/block_spec.rb0000644000175000017500000000373113715565705027017 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Block do let(:ast) do s(:block, body_a, body_b) end let(:object) do Morpher.compile(ast) end let(:evaluator_a) do Morpher.compile(body_a) end let(:evaluator_b) do Morpher.compile(body_b) end context 'transitive' do let(:body_a) do s(:guard, s(:primitive, String)) end let(:body_b) do s(:guard, s(:primitive, String)) end let(:valid_input) { 'foo' } let(:expected_output) { 'foo' } let(:invalid_input) { :foo } let(:expected_exception) do Morpher::Evaluator::Transformer::TransformError.new(object.body.first, invalid_input) end include_examples 'transitive evaluator' context 'with invalid input' do specify '#evaluation' do evaluation = object.evaluation(invalid_input) expect(evaluation.evaluations.length).to eql(1) end end end context 'intransitive' do let(:valid_input) { { 'foo' => 'bar' } } let(:invalid_input) { {} } let(:expected_output) { true } let(:body_a) do s(:key_fetch, 'foo') end let(:body_b) do s(:primitive, String) end let(:expected_exception) do Morpher::Evaluator::Transformer::TransformError.new(object.body.first, invalid_input) end include_examples 'intransitive evaluator' context '#evaluation' do subject { object.evaluation(valid_input) } let(:evaluations) do [ evaluator_a.evaluation(valid_input), evaluator_b.evaluation('bar') ] end context 'with valid input' do it 'returns evaluation' do should eql( Morpher::Evaluation::Nary.new( input: valid_input, evaluator: object, evaluations: evaluations, output: expected_output, success: true ) ) end end end end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/hash_transform_spec.rb0000644000175000017500000000217013715565705030737 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::HashTransform do let(:ast) do s(:hash_transform, body_a) end let(:object) do Morpher.compile(ast) end let(:evaluator_a) do Morpher.compile(body_a) end context 'intransitive' do let(:valid_input) { { 'foo' => 'bar' } } let(:invalid_input) { {} } let(:expected_output) { { foo: String } } let(:body_a) do s(:key_symbolize, 'foo', s(:attribute, :class)) end let(:expected_exception) do Morpher::Evaluator::Transformer::TransformError.new(object.body.first.body.first, invalid_input) end include_examples 'intransitive evaluator' end context 'transitive' do let(:body_a) do s(:key_symbolize, 'foo', s(:guard, s(:primitive, String))) end let(:valid_input) { { 'foo' => 'bar' } } let(:invalid_input) { {} } let(:expected_output) { { foo: 'bar' } } let(:expected_exception) do Morpher::Evaluator::Transformer::TransformError.new(object.body.first.body.first, invalid_input) end include_examples 'transitive evaluator' end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/guard_spec.rb0000644000175000017500000000060613715565705027025 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Guard do let(:object) { described_class.new(predicate) } let(:predicate) { Morpher.compile(s(:primitive, String)) } let(:valid_input) { 'foo' } let(:invalid_input) { :foo } let(:expected_output) { valid_input } include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/custom_spec.rb0000644000175000017500000000052713715565705027237 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Custom do let(:object) { described_class.new(param) } let(:param) { [a, b] } let(:a) { ->(_input) { :a } } let(:b) { ->(_input) { :b } } let(:valid_input) { double('Input') } let(:expected_output) { :a } include_examples 'transforming evaluator on valid input' end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/coerce/0000755000175000017500000000000013715565705025622 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/transformer/coerce/parse_int_spec.rb0000644000175000017500000000102413715565705031142 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Coerce::ParseInt do let(:object) { described_class.new(base) } context 'base 16' do let(:base) { 16 } include_examples 'transitive evaluator' let(:valid_input) { 'ff' } let(:invalid_input) { '0xfg' } let(:expected_output) { 255 } end context 'base 10' do let(:base) { 10 } include_examples 'transitive evaluator' let(:valid_input) { '100' } let(:invalid_input) { '100.0' } let(:expected_output) { 100 } end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/input_spec.rb0000644000175000017500000000051113715565705027055 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Input do let(:object) { described_class.new } let(:valid_input) { double('Input') } let(:expected_output) { valid_input } include_examples 'transforming evaluator on valid input' include_examples 'inverse evaluator' do let(:expected_inverse) { object } end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/attribute_spec.rb0000644000175000017500000000043013715565705027721 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Attribute do let(:object) { described_class.new(:length) } include_examples 'transforming evaluator on valid input' include_examples 'intransitive evaluator' let(:valid_input) { 'foo' } let(:expected_output) { 3 } end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/map_spec.rb0000644000175000017500000000131013715565705026471 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Map do let(:object) { described_class.new(operand) } context '#intransitive' do let(:operand) { Morpher.compile(s(:attribute, :length)) } include_examples 'transforming evaluator on valid input' include_examples 'intransitive evaluator' let(:valid_input) { ['foo'] } let(:expected_output) { [3] } end context '#transitive' do let(:operand) { Morpher.compile(s(:guard, s(:primitive, String))) } include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' let(:valid_input) { ['foo'] } let(:invalid_input) { [nil] } let(:expected_output) { ['foo'] } end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/domain/0000755000175000017500000000000013715565705025631 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/evaluator/transformer/domain/attribute_accessors_spec.rb0000644000175000017500000000215613715565705033244 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Domain::AttributeAccessors do let(:model) do Class.new do include Equalizer.new(:foo, :bar) attr_accessor :foo, :bar end end let(:param) do described_class::Param.new(model, %i[foo bar]) end describe Morpher::Evaluator::Transformer::Domain::AttributeAccessors::Dump do let(:object) { described_class::Dump.new(param) } let(:expected_output) { { foo: :foo, bar: :bar } } let(:valid_input) do object = model.allocate object.foo = :foo object.bar = :bar object end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end describe Morpher::Evaluator::Transformer::Domain::AttributeAccessors::Load do let(:object) { described_class::Load.new(param) } let(:valid_input) { { foo: :foo, bar: :bar } } let(:expected_output) do object = model.allocate object.foo = :foo object.bar = :bar object end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/domain/instance_variables_spec.rb0000644000175000017500000000215213715565705033024 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Domain::InstanceVariables do let(:model) do Class.new do include Equalizer.new(:foo, :bar) attr_accessor :foo, :bar end end let(:param) do described_class::Param.new(model, %i[foo bar]) end describe Morpher::Evaluator::Transformer::Domain::InstanceVariables::Dump do let(:object) { described_class::Dump.new(param) } let(:expected_output) { { foo: :foo, bar: :bar } } let(:valid_input) do object = model.allocate object.foo = :foo object.bar = :bar object end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end describe Morpher::Evaluator::Transformer::Domain::InstanceVariables::Load do let(:object) { described_class::Load.new(param) } let(:valid_input) { { foo: :foo, bar: :bar } } let(:expected_output) do object = model.allocate object.foo = :foo object.bar = :bar object end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/domain/attribute_hash_spec.rb0000644000175000017500000000172113715565705032177 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Domain::AttributeHash do let(:model) do Class.new do include Anima.new(:foo, :bar) end end let(:param) do described_class::Param.new(model, %i[foo bar]) end describe Morpher::Evaluator::Transformer::Domain::AttributeHash::Dump do let(:object) { described_class::Dump.new(param) } let(:expected_output) { { foo: :foo, bar: :bar } } let(:valid_input) do model.new(foo: :foo, bar: :bar) end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end describe Morpher::Evaluator::Transformer::Domain::AttributeHash::Load do let(:object) { described_class::Load.new(param) } let(:valid_input) { { foo: :foo, bar: :bar } } let(:expected_output) do model.new(foo: :foo, bar: :bar) end include_examples 'transforming evaluator on valid input' include_examples 'transitive evaluator' end end morpher-0.2.6/spec/unit/morpher/evaluator/transformer/static_spec.rb0000644000175000017500000000043613715565705027213 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator::Transformer::Static do let(:object) { described_class.new(value) } let(:value) { double('Value') } let(:valid_input) { double('Input') } let(:expected_output) { value } include_examples 'transforming evaluator on valid input' end morpher-0.2.6/spec/unit/morpher/compiler/0000755000175000017500000000000013715565705021630 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/unit/morpher/compiler/preprocessor_spec.rb0000644000175000017500000000204013715565705025711 0ustar debbiecocoadebbiecocoadescribe Morpher::Compiler::Preprocessor do subject { Morpher::Compiler::Preprocessor::DEFAULT.call(input) } shared_examples_for 'a preprocessor' do it { should eql(expected) } it 'is able to compile output' do Morpher.compile(subject) end end context 'with s(:boolean)' do it_should_behave_like 'a preprocessor' do let(:input) { s(:boolean) } let(:expected) { s(:xor, s(:primitive, TrueClass), s(:primitive, FalseClass)) } end end class Example include Anima.new(:foo, :bar) end let(:param) do Morpher::Evaluator::Transformer::Domain::Param.new(Example, %i[foo bar]) end context 'with s(:anima_load)' do it_should_behave_like 'a preprocessor' do let(:input) { s(:anima_load, Example) } let(:expected) { s(:load_attribute_hash, param) } end end context 'with s(:anima_dump)' do it_should_behave_like 'a preprocessor' do let(:input) { s(:anima_dump, Example) } let(:expected) { s(:dump_attribute_hash, param) } end end end morpher-0.2.6/spec/unit/morpher/evaluator_spec.rb0000644000175000017500000000054213715565705023360 0ustar debbiecocoadebbiecocoadescribe Morpher::Evaluator do describe '.register' do let(:object) do Class.new(described_class) do public_class_method :register end end subject { object.register(:foo) } it 'registers evaluator' do expect { subject }.to change { Morpher::Evaluator::REGISTRY[:foo] }.from(nil).to(object) end end end morpher-0.2.6/spec/integration_spec.rb0000644000175000017500000001137713715565705021256 0ustar debbiecocoadebbiecocoadescribe Morpher do class Foo include Anima.new(:attribute_a, :attribute_b) end class Bar include Anima.new(:baz) end class Baz include Anima.new(:value) end let(:tree_a) do Foo.new( attribute_a: Baz.new(value: :value_a), attribute_b: :value_b ) end let(:transformer_ast) do s( :block, s(:guard, s(:primitive, Hash)), s( :hash_transform, s( :key_symbolize, :attribute_a, s(:guard, s(:primitive, String)) ), s( :key_symbolize, :attribute_b, s(:guard, s(:primitive, Fixnum)) ) ), s(:load_attribute_hash, s(:param, Foo)) ) end let(:predicate_ast) do s( :block, s(:key_fetch, :attribute_a), s(:eql, s(:static, 'foo'), s(:input)) ) end specify 'allows to execute a transformation' do evaluator = Morpher.compile(transformer_ast) valid = { 'attribute_a' => 'a string', 'attribute_b' => 8015 } expect(evaluator.call(valid)).to eql( Foo.new(attribute_a: 'a string', attribute_b: 8015) ) evaluation = evaluator.evaluation(valid) expect(evaluation.output).to eql( Foo.new(attribute_a: 'a string', attribute_b: 8015) ) invalid = { 'attribute_a' => 0, 'attribute_b' => 8015 } expect { evaluator.call(invalid) }.to raise_error(Morpher::Evaluator::Transformer::TransformError) evaluation = evaluator.evaluation(invalid) expect(evaluation.success?).to be(false) end specify 'allows to inverse a transformations' do evaluator = Morpher.compile(transformer_ast) expect(evaluator.inverse.inverse).to eql(evaluator) input = Foo.new(attribute_a: 'a string', attribute_b: 8015) valid = { 'attribute_a' => 'a string', 'attribute_b' => 8015 } expect(evaluator.inverse.call(input)).to eql(valid) end specify 'allows to merge inputs' do evaluator = Morpher.compile(s(:merge, foo: :bar)) expect(evaluator.call(foo: :bar)).to eql(foo: :bar) expect(evaluator.call(bar: :baz)).to eql(foo: :bar, bar: :baz) end specify 'allows to coerce inputs from string to int and back' do evaluator = Morpher.compile(s(:parse_int, 10)) expect(evaluator.call('42')).to be(42) expect(evaluator.inverse.call(42)).to eql('42') evaluator = Morpher.compile(s(:int_to_string, 10)) expect(evaluator.call(42)).to eql('42') expect(evaluator.inverse.call('42')).to be(42) end specify 'allows to coerce inputs from ISO8601 string to DateTime and back' do evaluator = Morpher.compile(s(:parse_iso8601_date_time, 0)) iso8601_string = '2014-08-04T00:00:00+00:00' date_time = DateTime.new(2014, 8, 4) expect(evaluator.call(iso8601_string)).to eq(date_time) expect(evaluator.inverse.call(date_time)).to eq(iso8601_string) evaluator = Morpher.compile(s(:date_time_to_iso8601_string, 0)) expect(evaluator.call(date_time)).to eq(iso8601_string) expect(evaluator.inverse.call(iso8601_string)).to eq(date_time) end specify 'allows custom transformations' do evaluator = Morpher.compile(s(:custom, [->(v) { "changed_#{v}" }])) expect(evaluator.call('test')).to eql('changed_test') end specify 'allows predicates to be run from sexp' do valid = { attribute_a: 'foo' } invalid = { attribute_a: 'bar' } evaluator = Morpher.compile(predicate_ast) expect(evaluator.call(valid)).to be(true) expect(evaluator.call(invalid)).to be(false) evaluation = evaluator.evaluation(valid) expect(evaluation.output).to be(true) expect(evaluation.input).to be(valid) expect(evaluation.description).to eql(strip(<<-TEXT)) Morpher::Evaluation::Nary input: {:attribute_a=>"foo"} output: true success?: true evaluator: Morpher::Evaluator::Transformer::Block evaluations: Morpher::Evaluation::Nullary input: {:attribute_a=>"foo"} output: "foo" success?: true evaluator: Morpher::Evaluator::Transformer::Key::Fetch param: :attribute_a Morpher::Evaluation::Binary input: "foo" output: true success?: true left_evaluation: Morpher::Evaluation::Nullary input: "foo" output: "foo" success?: true evaluator: Morpher::Evaluator::Transformer::Static param: "foo" right_evaluation: Morpher::Evaluation::Nullary input: "foo" output: "foo" success?: true evaluator: Morpher::Evaluator::Transformer::Input TEXT end end morpher-0.2.6/spec/spec_helper.rb0000644000175000017500000000136513715565705020206 0ustar debbiecocoadebbiecocoarequire 'devtools/spec_helper' require 'morpher' require 'mutant' # for the node helpers # Monkeypatch to mutant all specs per mutation. # # TODO: Use master once it supports configurable implicit coverage. # # Morpher predicates are needed to finally make this configurable in mutant. # module Mutant module Rspec class Killer # Return all example groups # # @return [Enumerable] # # @api private # def example_groups strategy.example_groups end end # Rspec end # Killer end # Mutant RSpec.configure do |config| config.include(StripHelper) config.include(Morpher::NodeHelpers) config.expect_with :rspec do |rspec| rspec.syntax = %i[expect should] end end morpher-0.2.6/spec/shared/0000755000175000017500000000000013715565705016631 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/shared/evaluator_behavior.rb0000644000175000017500000001105013715565705023034 0ustar debbiecocoadebbiecocoashared_examples_for 'inverse evaluator' do context '#inverse' do subject { object.inverse } it 'returns the expected inverse evaluator' do should eql(expected_inverse) end end end shared_examples_for 'evaluator' do it 'round trips transitive evaluators via #inverse' do if object.kind_of?(Morpher::Evaluator::Transformer) && object.transitive? object.inverse.inverse.should eql(object) end end it 'round trips evaluators via #node' do Morpher.compile(object.node).should eql(object) end end shared_examples_for 'predicate evaluator' do include_examples 'evaluator' let(:negative_example?) { true } let(:expected_output) { true } let(:valid_input) { positive_input } let(:expected_positive_output) { true } let(:expected_negative_output) { false } context 'with positive input' do it 'evaluates to positive output' do expect(object.call(positive_input)).to be(expected_positive_output) end it 'evaluates to inverted positive output' do expect(object.inverse.call(positive_input)).to be(!expected_positive_output) end it 'evaluates to the same output under #evaluation' do evaluation = object.evaluation(positive_input) expect(evaluation.success?).to be(true) expect(evaluation.input).to be(positive_input) expect(evaluation.output).to be(expected_positive_output) end end context 'with negative input' do it 'evaluates to false' do expect(object.call(negative_input)).to be(false) end it 'evaluates to true on inverse' do expect(object.inverse.call(negative_input)).to be(true) end it 'evaluates to the same output under #evaluation' do evaluation = object.evaluation(negative_input) expect(evaluation.input).to be(negative_input) expect(evaluation.success?).to be(true) expect(evaluation.output).to be(false) end end end shared_examples_for 'transitive evaluator' do include_examples 'evaluator' let(:invalid_input_example?) { true } it 'signals transitivity via #transitive?' do expect(object.transitive?).to be(true) end it 'round trips valid inputs via #evaluation' do evaluation = object.evaluation(valid_input) expect(evaluation.success?).to be(true) evaluation = object.inverse.evaluation(evaluation.output) expect(evaluation.output).to eql(valid_input) expect(evaluation.success?).to be(true) end it 'round trips valid inputs via #call' do forward = object.call(valid_input) expect(object.inverse.call(forward)).to eql(valid_input) end end shared_examples_for 'intransitive evaluator' do include_examples 'evaluator' let(:invalid_input_example?) { true } it 'signals intransitivity via #transitive?' do expect(object.transitive?).to be(false) end end shared_examples_for 'transforming evaluator on valid input' do include_examples 'evaluator' it 'transforms to expected output via #call' do result = object.call(valid_input) expect(result).to eql(expected_output) end it 'transforms to expected output via #evaluation' do evaluation = object.evaluation(valid_input) expect(evaluation.success?).to eql(true) expect(evaluation.output).to eql(expected_output) end specify '#evaluation' do evaluation = object.evaluation(valid_input) expect(evaluation.success?).to be(true) expect(evaluation.evaluator).to eql(object) expect(evaluation.input).to eql(valid_input) expect(evaluation.output).to eql(expected_output) end specify '#call' do expect(object.call(valid_input)).to eql(expected_output) end end shared_examples_for 'transforming evaluator on invalid input' do it 'raises error for #call' do expect { object.call(invalid_input) }.to raise_error( Morpher::Evaluator::Transformer::TransformError ) end it 'returns error evaluator for #evaluation' do evaluation = object.evaluation(invalid_input) expect(evaluation.success?).to eql(false) expect(evaluation.output).to be(Morpher::Undefined) end let(:expected_exception) do Morpher::Evaluator::Transformer::TransformError.new(object, invalid_input) end specify '#call' do expect { object.call(invalid_input) }.to raise_error(expected_exception) end specify '#evaluation' do evaluation = object.evaluation(invalid_input) expect(evaluation.success?).to be(false) expect(evaluation.evaluator).to eql(object) expect(evaluation.input).to eql(invalid_input) expect(evaluation.output).to eql(Morpher::Undefined) end end morpher-0.2.6/spec/support/0000755000175000017500000000000013715565705017077 5ustar debbiecocoadebbiecocoamorpher-0.2.6/spec/support/ice_nine_config.rb0000644000175000017500000000014013715565705022515 0ustar debbiecocoadebbiecocoarequire 'ice_nine' module IceNine class Freezer class RSpec < NoFreeze end end end morpher-0.2.6/spec/support/let_mock_helper.rb0000644000175000017500000000027013715565705022557 0ustar debbiecocoadebbiecocoamodule LetMockHelper def let_mock(name, &block) let(name) do stubs = block ? instance_exec(double, &block) : {} double(name.to_s.capitalize, stubs) end end end morpher-0.2.6/spec/support/strip_helper.rb0000644000175000017500000000040613715565705022124 0ustar debbiecocoadebbiecocoamodule StripHelper def strip(text) return text if text.empty? lines = text.lines match = /\A[ ]*/.match(lines.first) range = match[0].length..-1 source = lines.map do |line| line[range] end.join source.chomp << "\n" end end morpher-0.2.6/spec/rcov.opts0000644000175000017500000000015713715565705017246 0ustar debbiecocoadebbiecocoa--exclude-only "spec/,^/" --sort coverage --callsites --xrefs --profile --text-summary --failure-threshold 100