abstract_mapper 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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