abstract_mapper 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +19 -0
  8. data/.yardopts +3 -0
  9. data/CHANGELOG.md +3 -0
  10. data/Gemfile +9 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE +21 -0
  13. data/README.md +239 -0
  14. data/Rakefile +34 -0
  15. data/abstract_mapper.gemspec +26 -0
  16. data/config/metrics/STYLEGUIDE +230 -0
  17. data/config/metrics/cane.yml +5 -0
  18. data/config/metrics/churn.yml +6 -0
  19. data/config/metrics/flay.yml +2 -0
  20. data/config/metrics/metric_fu.yml +15 -0
  21. data/config/metrics/reek.yml +1 -0
  22. data/config/metrics/roodi.yml +24 -0
  23. data/config/metrics/rubocop.yml +76 -0
  24. data/config/metrics/saikuro.yml +3 -0
  25. data/config/metrics/simplecov.yml +6 -0
  26. data/config/metrics/yardstick.yml +37 -0
  27. data/lib/abstract_mapper/branch.rb +110 -0
  28. data/lib/abstract_mapper/builder.rb +84 -0
  29. data/lib/abstract_mapper/commands.rb +71 -0
  30. data/lib/abstract_mapper/dsl.rb +62 -0
  31. data/lib/abstract_mapper/errors/unknown_command.rb +25 -0
  32. data/lib/abstract_mapper/errors/wrong_node.rb +23 -0
  33. data/lib/abstract_mapper/errors/wrong_rule.rb +23 -0
  34. data/lib/abstract_mapper/functions.rb +76 -0
  35. data/lib/abstract_mapper/node.rb +76 -0
  36. data/lib/abstract_mapper/optimizer.rb +48 -0
  37. data/lib/abstract_mapper/pair_rule.rb +66 -0
  38. data/lib/abstract_mapper/rspec.rb +7 -0
  39. data/lib/abstract_mapper/rule.rb +52 -0
  40. data/lib/abstract_mapper/rules.rb +61 -0
  41. data/lib/abstract_mapper/settings.rb +95 -0
  42. data/lib/abstract_mapper/sole_rule.rb +58 -0
  43. data/lib/abstract_mapper/version.rb +9 -0
  44. data/lib/abstract_mapper.rb +82 -0
  45. data/lib/rspec/functions.rb +25 -0
  46. data/lib/rspec/mapper.rb +40 -0
  47. data/lib/rspec/nodes.rb +58 -0
  48. data/lib/rspec/rules.rb +59 -0
  49. data/spec/integration/faceter.rb +62 -0
  50. data/spec/integration/mapper_definition_spec.rb +33 -0
  51. data/spec/integration/rspec_examples_spec.rb +77 -0
  52. data/spec/spec_helper.rb +20 -0
  53. data/spec/unit/abstract_mapper/branch_spec.rb +126 -0
  54. data/spec/unit/abstract_mapper/builder_spec.rb +78 -0
  55. data/spec/unit/abstract_mapper/commands_spec.rb +105 -0
  56. data/spec/unit/abstract_mapper/dsl_spec.rb +82 -0
  57. data/spec/unit/abstract_mapper/errors/unknown_command_spec.rb +21 -0
  58. data/spec/unit/abstract_mapper/errors/wrong_node_spec.rb +21 -0
  59. data/spec/unit/abstract_mapper/errors/wrong_rule_spec.rb +23 -0
  60. data/spec/unit/abstract_mapper/functions/compact_spec.rb +25 -0
  61. data/spec/unit/abstract_mapper/functions/filter_spec.rb +26 -0
  62. data/spec/unit/abstract_mapper/functions/subclass_spec.rb +66 -0
  63. data/spec/unit/abstract_mapper/node_spec.rb +84 -0
  64. data/spec/unit/abstract_mapper/optimizer_spec.rb +48 -0
  65. data/spec/unit/abstract_mapper/pair_rule_spec.rb +59 -0
  66. data/spec/unit/abstract_mapper/rule_spec.rb +83 -0
  67. data/spec/unit/abstract_mapper/rules_spec.rb +88 -0
  68. data/spec/unit/abstract_mapper/settings_spec.rb +113 -0
  69. data/spec/unit/abstract_mapper/sole_rule_spec.rb +51 -0
  70. data/spec/unit/abstract_mapper_spec.rb +36 -0
  71. metadata +171 -0
@@ -0,0 +1,24 @@
1
+ ---
2
+ AssignmentInConditionalCheck:
3
+ CaseMissingElseCheck:
4
+ ClassLineCountCheck:
5
+ line_count: 100
6
+ ClassNameCheck:
7
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
8
+ ClassVariableCheck:
9
+ CyclomaticComplexityBlockCheck:
10
+ complexity: 3
11
+ CyclomaticComplexityMethodCheck:
12
+ complexity: 3
13
+ EmptyRescueBodyCheck:
14
+ ForLoopCheck:
15
+ MethodLineCountCheck:
16
+ line_count: 7
17
+ MethodNameCheck:
18
+ pattern: !ruby/regexp /^[\||\^|\&|\!]$|^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
19
+ ModuleLineCountCheck:
20
+ line_count: 100
21
+ ModuleNameCheck:
22
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
23
+ ParameterNumberCheck:
24
+ parameter_count: 3
@@ -0,0 +1,76 @@
1
+ ---
2
+ # settings added by the 'hexx-suit' module
3
+ # output: "tmp/rubocop"
4
+ # format: "html"
5
+
6
+ AllCops:
7
+ Exclude:
8
+ - '**/db/schema.rb'
9
+
10
+ Lint/HandleExceptions:
11
+ Exclude:
12
+ - '**/*_spec.rb'
13
+
14
+ Lint/RescueException:
15
+ Exclude:
16
+ - '**/*_spec.rb'
17
+
18
+ Metrics/LineLength:
19
+ Exclude:
20
+ - '**/integration/*_spec.rb'
21
+
22
+ Style/AccessorMethodName:
23
+ Exclude:
24
+ - '**/*_spec.rb'
25
+
26
+ Style/AsciiComments:
27
+ Enabled: false
28
+
29
+ Style/ClassAndModuleChildren:
30
+ Enabled: false
31
+
32
+ Style/Documentation:
33
+ Enabled: false
34
+
35
+ Style/EmptyLinesAroundBlockBody:
36
+ Enabled: false
37
+
38
+ Style/EmptyLinesAroundClassBody:
39
+ Enabled: false
40
+
41
+ Style/EmptyLinesAroundMethodBody:
42
+ Enabled: false
43
+
44
+ Style/EmptyLinesAroundModuleBody:
45
+ Enabled: false
46
+
47
+ Style/EmptyLineBetweenDefs:
48
+ Enabled: false
49
+
50
+ Style/FileName:
51
+ Enabled: false
52
+
53
+ Style/RaiseArgs:
54
+ EnforcedStyle: compact
55
+
56
+ Style/SingleLineMethods:
57
+ Exclude:
58
+ - '**/*_spec.rb'
59
+
60
+ Style/SingleSpaceBeforeFirstArg:
61
+ Enabled: false
62
+
63
+ Style/SpecialGlobalVars:
64
+ Exclude:
65
+ - '**/Gemfile'
66
+ - '**/*.gemspec'
67
+
68
+ Style/StringLiterals:
69
+ EnforcedStyle: double_quotes
70
+
71
+ Style/StringLiteralsInInterpolation:
72
+ EnforcedStyle: double_quotes
73
+
74
+ Style/TrivialAccessors:
75
+ Exclude:
76
+ - '**/*_spec.rb'
@@ -0,0 +1,3 @@
1
+ ---
2
+ warn_cyclo: 2
3
+ error_cyclo: 3
@@ -0,0 +1,6 @@
1
+ ---
2
+ output: tmp/coverage
3
+ filters: # The list of paths to be excluded from coverage checkup
4
+ - "spec/"
5
+ - "config/"
6
+ groups: []
@@ -0,0 +1,37 @@
1
+ ---
2
+ # Settings added by the 'hexx-suit' gem
3
+ output: "tmp/yardstick/output.log"
4
+ path: "lib/**/*.rb"
5
+ rules:
6
+ ApiTag::Presence:
7
+ enabled: true
8
+ exclude: []
9
+ ApiTag::Inclusion:
10
+ enabled: true
11
+ exclude: []
12
+ ApiTag::ProtectedMethod:
13
+ enabled: true
14
+ exclude: []
15
+ ApiTag::PrivateMethod:
16
+ enabled: false
17
+ exclude: []
18
+ ExampleTag:
19
+ enabled: true
20
+ exclude: []
21
+ ReturnTag:
22
+ enabled: true
23
+ exclude: []
24
+ Summary::Presence:
25
+ enabled: true
26
+ exclude: []
27
+ Summary::Length:
28
+ enabled: true
29
+ exclude: []
30
+ Summary::Delimiter:
31
+ enabled: true
32
+ exclude: []
33
+ Summary::SingleLine:
34
+ enabled: true
35
+ exclude: []
36
+ threshold: 100
37
+ verbose: false
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # A special type of the composed node, that describes transformation,
6
+ # applied to some level of nested input.
7
+ #
8
+ # Unlike the simple node, describing a transformation of data, the
9
+ # branch carries a collection of subnodes along with methods to [#rebuild]
10
+ # itself with the same attributes and different subnodes.
11
+ #
12
+ # Tne branch only stores subnodes and composes transformations.
13
+ # Its has no access to DSL and knows neither how to build a tree
14
+ # (see [AbstractMapper::Builder]), nor how to optimize it later
15
+ # (see [AbstractMapper::Optimizer]).
16
+ #
17
+ # @api public
18
+ #
19
+ class Branch < Node
20
+
21
+ include Enumerable
22
+
23
+ # @!scope class
24
+ # @!method new(*attributes, &block)
25
+ # Creates a new branch
26
+ #
27
+ # @param [Object, Array<Object>] attributes
28
+ # The list of type-specific attributes of the branch
29
+ # @param [Proc] block
30
+ # The block that returns an array of subnodes for the branch
31
+ #
32
+ # @return [Branch::Node]
33
+
34
+ # @private
35
+ def initialize(*attributes)
36
+ @subnodes = block_given? ? yield : []
37
+ super(*attributes, &nil)
38
+ end
39
+
40
+ # Returns a new branch of the same type, with the same attributes,
41
+ # but with a different collection of subnodes, transmitted by the block.
42
+ #
43
+ # @example
44
+ # branch = Branch.new(:foo)
45
+ # # => <Branch(:foo) []>
46
+ # branch.rebuild { Node.new(:bar) }
47
+ # # => <Branch(:foo) [<Node(:bar)>]>
48
+ #
49
+ # @param [Proc] block
50
+ # The block that should return an array of subnodes for the branch
51
+ #
52
+ # @return [AbstractMapper::Branch]
53
+ #
54
+ # @yield block
55
+ #
56
+ def rebuild(&block)
57
+ self.class.new(*attributes, &block)
58
+ end
59
+
60
+ # @!method each
61
+ # Returns the enumerator for subnodes
62
+ #
63
+ # @return [Enumerator]
64
+ #
65
+ def each(&block)
66
+ @subnodes.each(&block)
67
+ end
68
+
69
+ # Returns a new branch with the other node added to its subnodes
70
+ #
71
+ # @param [AbstractMapper::Node] other
72
+ #
73
+ # @return [AbstractMapper::Branch]
74
+ #
75
+ def <<(other)
76
+ rebuild { entries << other }
77
+ end
78
+
79
+ # The composition of transformations from all subnodes of the branch
80
+ #
81
+ # To be reloaded by the subclasses to apply the composition to
82
+ # a corresponding level of nested data.
83
+ #
84
+ # @return [Transproc::Function]
85
+ #
86
+ # @abstract
87
+ #
88
+ def transproc
89
+ map(&:transproc).inject(:>>)
90
+ end
91
+
92
+ # Adds subnodes to the default description of the branch
93
+ #
94
+ # @return [String]
95
+ #
96
+ def to_s
97
+ "#{super} [#{map(&:inspect).join(", ")}]"
98
+ end
99
+
100
+ private
101
+
102
+ # Substitutes the name of the class by the special name "Root"
103
+ # to describe the root node of AST.
104
+ def __name__
105
+ instance_of?(Branch) ? "Root" : super
106
+ end
107
+
108
+ end # class Branch
109
+
110
+ end # class AbstractMapper
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # Builds the immutable abstract syntax tree (AST) using DSL commands.
6
+ #
7
+ # @example
8
+ # Builder.update do
9
+ # field :user do # DSL method
10
+ # add_prefix :user # DSL method for subtree
11
+ # end
12
+ # end
13
+ # # => <Root() [<Field(:user) [<AddPrefix(:user)>]>]>
14
+ #
15
+ # @api private
16
+ #
17
+ class Builder
18
+
19
+ class << self
20
+
21
+ # @!attribute [rw] commands
22
+ #
23
+ # @return [AbstractMapper::Commands] The registry of DSL commands
24
+ #
25
+ attr_accessor :commands
26
+
27
+ end # eigenclass
28
+
29
+ # Updates given node by adding its subnode from the block
30
+ #
31
+ # @param [AbstractMapper::Branch] node
32
+ # The root of the tree to be updated
33
+ # @param [Proc] block
34
+ # The block with DSL commands for adding subnodes
35
+ #
36
+ # @return (see #tree)
37
+ #
38
+ def self.update(node = Branch.new, &block)
39
+ new(node, &block).tree
40
+ end
41
+
42
+ # @!attribute [r] tree
43
+ #
44
+ # @return [AbstractMapper::Branch] The updated tree
45
+ #
46
+ attr_reader :tree
47
+
48
+ # @!scope class
49
+ # @!method new(node, &block)
50
+ # Builds the AST by recursively adding new nodes, using the commands
51
+ #
52
+ # @param [AbstractMapper::Branch] node
53
+ # The root of the tree to be updated
54
+ # @param [Proc] block
55
+ # The block with DSL commands for adding subnodes
56
+ #
57
+ # @return [AbstractMapper::Builder]
58
+
59
+ # @private
60
+ def initialize(node, &block)
61
+ @tree = node
62
+ @commands = self.class.commands
63
+ instance_eval(&block) if block_given?
64
+ freeze
65
+ end
66
+
67
+ private # DSL commands
68
+
69
+ def method_missing(name, *args, &block)
70
+ node = @commands[name, *args, &block]
71
+ @tree = tree << (node.is_a?(Branch) ? update(node, &block) : node)
72
+ end
73
+
74
+ def respond_to_missing?(*)
75
+ true
76
+ end
77
+
78
+ def update(node, &block)
79
+ self.class.update(node, &block)
80
+ end
81
+
82
+ end # class Builder
83
+
84
+ end # class AbstractMapper
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # Registry of DSL commands used by the builder
6
+ #
7
+ # @api private
8
+ #
9
+ class Commands
10
+
11
+ # @!attribute [r] registry
12
+ #
13
+ # @return [Hash<Symbol => Class>]
14
+ # The registry of command names, pointing to types of the nodes,
15
+ # that should be built for adding to AST
16
+ #
17
+ attr_reader :registry
18
+
19
+ # @!scope class
20
+ # @!method new(registry = {})
21
+ # Creates an immutable collection of commands
22
+ #
23
+ # @param [Hash] registry
24
+ #
25
+ # @return [Faceter::Commands]
26
+
27
+ # @private
28
+ def initialize(registry = {})
29
+ @registry = registry.dup.freeze
30
+ freeze
31
+ end
32
+
33
+ # Returns a new immutable registry with added command name and type
34
+ #
35
+ # @param [Array<Symbol, Class>] other
36
+ #
37
+ # @return [undefined]
38
+ #
39
+ def <<(other)
40
+ name = other[0]
41
+ node = other[1]
42
+ self.class.new registry.merge(name.to_sym => node)
43
+ end
44
+
45
+ # Builds a node by the name of DSL command
46
+ #
47
+ # Skips the block if a registered node is a branch
48
+ #
49
+ # @param [#to_sym] name The name of the command
50
+ # @param [Object, Array] args The command's arguments
51
+ # @param [Proc] block
52
+ # The block to be passed to the constructor of the simple node
53
+ #
54
+ # @raise [AbstractMapper::Errors::UnknownCommand]
55
+ # When unknown command is called
56
+ #
57
+ def [](name, *args, &block)
58
+ type = get(name)
59
+ branch = Functions[:subclass?, Branch][type]
60
+ branch ? type.new(*args) : type.new(*args, &block)
61
+ end
62
+
63
+ private
64
+
65
+ def get(name)
66
+ registry.fetch(name.to_sym) { fail(Errors::UnknownCommand.new name) }
67
+ end
68
+
69
+ end # class Commands
70
+
71
+ end # class AbstractMapper
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # Provides features to configure mappers
6
+ #
7
+ # @api private
8
+ #
9
+ module DSL
10
+
11
+ # @private
12
+ def inherited(klass)
13
+ klass.instance_variable_set :@settings, settings
14
+ end
15
+
16
+ # @!attribute [r] settings
17
+ #
18
+ # @return [AbstractMapper::Settings]
19
+ # The configurable container of domain-specific settings
20
+ # (DSL commands and optimization rules along with corresponding
21
+ # AST builder and optimizer).
22
+ #
23
+ attr_reader :settings
24
+
25
+ # Configures domain-specific settings
26
+ #
27
+ # @param [Proc] block The block where rules and commands to be registered
28
+ #
29
+ # @return [self] itself
30
+ #
31
+ # @yield a block
32
+ #
33
+ def configure(&block)
34
+ @settings = Settings.new(&block)
35
+ self
36
+ end
37
+
38
+ # Returns the optimized AST
39
+ #
40
+ # @return [AbstractMapper::Branch]
41
+ #
42
+ def finalize
43
+ settings.optimizer.update(tree)
44
+ end
45
+
46
+ private # DSL commands
47
+
48
+ def tree
49
+ @tree ||= Branch.new
50
+ end
51
+
52
+ def respond_to_missing?(*)
53
+ true
54
+ end
55
+
56
+ def method_missing(name, *args, &block)
57
+ @tree = settings.builder.update(tree) { public_send(name, *args, &block) }
58
+ end
59
+
60
+ end # module DSL
61
+
62
+ end # class AbstractMapper
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # The collection of gem-specific errors
6
+ #
7
+ module Errors
8
+
9
+ # An exception to be raised when unknown DSL methods is used
10
+ #
11
+ # @api public
12
+ #
13
+ class UnknownCommand < NameError
14
+
15
+ # @private
16
+ def initialize(name)
17
+ super "'#{name}' is not a registered DSL command"
18
+ freeze
19
+ end
20
+
21
+ end # class UnknownCommand
22
+
23
+ end # module Errors
24
+
25
+ end # class AbstractMapper
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ module Errors
6
+
7
+ # An exception to be raised when wrong node is registered as a DSL command
8
+ #
9
+ # @api public
10
+ #
11
+ class WrongNode < TypeError
12
+
13
+ # @private
14
+ def initialize(node)
15
+ super "#{node} is not a subclass of AbstractMapper::Node"
16
+ freeze
17
+ end
18
+
19
+ end # class WrongNode
20
+
21
+ end # module Errors
22
+
23
+ end # class AbstractMapper
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ module Errors
6
+
7
+ # An exception to be raised when wrong node is registered as a DSL command
8
+ #
9
+ # @api public
10
+ #
11
+ class WrongRule < TypeError
12
+
13
+ # @private
14
+ def initialize(node)
15
+ super "#{node} is not a subclass of AbstractMapper::SoleRule"
16
+ freeze
17
+ end
18
+
19
+ end # class WrongRule
20
+
21
+ end # module Errors
22
+
23
+ end # class AbstractMapper
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # The collection of gem-specific pure functions (transproc)
6
+ #
7
+ # @api private
8
+ #
9
+ module Functions
10
+
11
+ extend Transproc::Registry
12
+
13
+ uses :map_array, from: Transproc::ArrayTransformations
14
+
15
+ # Applies the function to every element of array and removes empty values
16
+ #
17
+ # @example
18
+ # fn = Functions[:filter, -> v { v - 1 if v > 3 }]
19
+ # fn[[1, 4, 5, 3, 2, 5, 9]]
20
+ # # => [3, 4, 4, 8]
21
+ #
22
+ # @param [Array] array
23
+ # @param [Proc] fn
24
+ #
25
+ # @return [Array]
26
+ #
27
+ def filter(array, fn)
28
+ map_array(array, fn).compact.flatten
29
+ end
30
+
31
+ # Applies the function to every consecutive pair of array elements,
32
+ # and removes empty values
33
+ #
34
+ # @example
35
+ # function = -> a, b { (a == b) ? [a + b] : [a, b] }
36
+ # fn = Functions[:compact, function]
37
+ # fn[[1, 1, 2]] # => [4]
38
+ # fn[[1, 2, 2]] # => [1, 4]
39
+ #
40
+ # @param [Array] array
41
+ # @param [Proc] fn
42
+ # Anonymous function (proc, lambda), that takes two arguments
43
+ # and returns an array
44
+ #
45
+ # @return [Array]
46
+ #
47
+ def compact(array, fn)
48
+ array.each_with_object([]) do |i, a|
49
+ if a.empty?
50
+ a << i
51
+ else
52
+ a[-1] = fn.call(a.last, i)
53
+ a.flatten!
54
+ end
55
+ end
56
+ end
57
+
58
+ # Checks whether the class or module has given ancestor
59
+ #
60
+ # @example
61
+ # fn = Functions[:subclass?, Module]
62
+ # fn[Class] # => true
63
+ # fn[Object] # => false
64
+ #
65
+ # @param [Module] subling
66
+ # @param [Module] ancestor
67
+ #
68
+ # @return [Boolean]
69
+ #
70
+ def subclass?(subling, ancestor)
71
+ subling.ancestors.include?(ancestor)
72
+ end
73
+
74
+ end # module Functions
75
+
76
+ end # class AbstractMapper
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ class AbstractMapper
4
+
5
+ # An immutable node of the abstract syntax tree (AST), that describes
6
+ # either some "end-up" transformation, or a level of nested input data.
7
+ #
8
+ # Every node is expected to accept attributes and (possibly) block, and
9
+ # implement `#transproc` that implements the `#call` method to
10
+ # transform input data to the output.
11
+ #
12
+ # Nodes describe only the structure of AST, they know
13
+ # neither how to build the tree with DSL (see [AbstractMapper::Builder]),
14
+ # nor how to optimize it later (see [AbstractMapper::Optimizer]).
15
+ #
16
+ # @api public
17
+ #
18
+ class Node
19
+
20
+ # @!attribute [r] attributes
21
+ #
22
+ # @return [Array] The list of node-specific attributes
23
+ #
24
+ attr_reader :attributes
25
+
26
+ # @!attribute [r] block
27
+ #
28
+ # @return [Proc] The block given to initializer
29
+ #
30
+ attr_reader :block
31
+
32
+ # @private
33
+ def initialize(*attributes, &block)
34
+ @attributes = attributes.freeze
35
+ @block = block
36
+ freeze
37
+ end
38
+
39
+ # @!method transproc
40
+ # The transformation function for the branch
41
+ #
42
+ # @abstract
43
+ #
44
+ def transproc
45
+ Transproc::Function.new(-> v { v }, {})
46
+ end
47
+
48
+ # Returns a human-readable string representating the node
49
+ #
50
+ # @return [String]
51
+ #
52
+ def inspect
53
+ "<#{self}>"
54
+ end
55
+
56
+ # Converts the node into string with its name and attributes
57
+ #
58
+ # @return [String]
59
+ #
60
+ def to_s
61
+ "#{__name__}#{__attributes__}"
62
+ end
63
+
64
+ private
65
+
66
+ def __name__
67
+ self.class.name.split("::").last
68
+ end
69
+
70
+ def __attributes__
71
+ "(#{attributes.map(&:inspect).join(", ")})" if attributes.any?
72
+ end
73
+
74
+ end # class Node
75
+
76
+ end # class AbstractMapper