representable 3.0.0 → 3.1.0

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 (105) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +17 -0
  3. data/CHANGES.md +25 -0
  4. data/Gemfile +4 -12
  5. data/LICENSE +1 -1
  6. data/README.md +6 -6
  7. data/Rakefile +1 -6
  8. data/TODO +1 -3
  9. data/TODO-4.0.md +72 -0
  10. data/lib/representable.rb +19 -25
  11. data/lib/representable/binding.rb +32 -12
  12. data/lib/representable/cached.rb +1 -1
  13. data/lib/representable/coercion.rb +8 -6
  14. data/lib/representable/config.rb +13 -3
  15. data/lib/representable/debug.rb +23 -15
  16. data/lib/representable/declarative.rb +12 -7
  17. data/lib/representable/decorator.rb +1 -1
  18. data/lib/representable/definition.rb +7 -3
  19. data/lib/representable/deserializer.rb +5 -4
  20. data/lib/representable/for_collection.rb +1 -1
  21. data/lib/representable/hash.rb +9 -2
  22. data/lib/representable/hash/allow_symbols.rb +9 -11
  23. data/lib/representable/hash/binding.rb +1 -0
  24. data/lib/representable/hash/collection.rb +4 -2
  25. data/lib/representable/hash_methods.rb +3 -2
  26. data/lib/representable/insert.rb +1 -1
  27. data/lib/representable/json.rb +8 -7
  28. data/lib/representable/json/collection.rb +3 -0
  29. data/lib/representable/object.rb +1 -1
  30. data/lib/representable/object/binding.rb +5 -1
  31. data/lib/representable/option.rb +19 -0
  32. data/lib/representable/pipeline.rb +3 -2
  33. data/lib/representable/pipeline_factories.rb +4 -2
  34. data/lib/representable/populator.rb +1 -1
  35. data/lib/representable/represent.rb +1 -0
  36. data/lib/representable/serializer.rb +3 -2
  37. data/lib/representable/version.rb +1 -1
  38. data/lib/representable/virtus_coercion.rb +38 -0
  39. data/lib/representable/xml.rb +12 -10
  40. data/lib/representable/xml/binding.rb +19 -13
  41. data/lib/representable/xml/namespace.rb +122 -0
  42. data/lib/representable/yaml.rb +6 -2
  43. data/lib/representable/yaml/binding.rb +1 -0
  44. data/representable.gemspec +8 -9
  45. data/test/as_test.rb +7 -7
  46. data/test/binding_test.rb +14 -14
  47. data/test/cached_test.rb +59 -49
  48. data/test/class_test.rb +9 -9
  49. data/test/coercion_test.rb +33 -22
  50. data/test/config/inherit_test.rb +14 -14
  51. data/test/config_test.rb +20 -20
  52. data/test/decorator_scope_test.rb +4 -4
  53. data/test/decorator_test.rb +33 -20
  54. data/test/default_test.rb +8 -8
  55. data/test/defaults_options_test.rb +3 -3
  56. data/test/definition_test.rb +38 -40
  57. data/test/{example.rb → examples/example.rb} +0 -1
  58. data/test/examples/object.rb +1 -5
  59. data/test/exec_context_test.rb +8 -8
  60. data/test/features_test.rb +6 -6
  61. data/test/filter_test.rb +8 -8
  62. data/test/for_collection_test.rb +10 -10
  63. data/test/generic_test.rb +13 -13
  64. data/test/getter_setter_test.rb +5 -5
  65. data/test/hash_bindings_test.rb +1 -1
  66. data/test/hash_test.rb +45 -23
  67. data/test/heritage_test.rb +16 -13
  68. data/test/if_test.rb +9 -9
  69. data/test/include_exclude_test.rb +14 -14
  70. data/test/inherit_test.rb +18 -18
  71. data/test/inline_test.rb +24 -24
  72. data/test/instance_test.rb +31 -31
  73. data/test/is_representable_test.rb +10 -10
  74. data/test/json_test.rb +29 -7
  75. data/test/lonely_test.rb +31 -31
  76. data/test/nested_test.rb +13 -13
  77. data/test/object_test.rb +9 -9
  78. data/test/option_test.rb +36 -0
  79. data/test/parse_pipeline_test.rb +3 -5
  80. data/test/pipeline_test.rb +50 -50
  81. data/test/populator_test.rb +18 -18
  82. data/test/prepare_test.rb +4 -4
  83. data/test/private_options_test.rb +2 -2
  84. data/test/reader_writer_test.rb +2 -2
  85. data/test/render_nil_test.rb +2 -2
  86. data/test/represent_test.rb +14 -14
  87. data/test/representable_test.rb +34 -36
  88. data/test/schema_test.rb +8 -11
  89. data/test/serialize_deserialize_test.rb +2 -2
  90. data/test/skip_test.rb +14 -14
  91. data/test/stringify_hash_test.rb +3 -3
  92. data/test/test_helper.rb +26 -14
  93. data/test/uncategorized_test.rb +10 -10
  94. data/test/user_options_test.rb +4 -4
  95. data/test/virtus_coercion_test.rb +52 -0
  96. data/test/wrap_test.rb +19 -19
  97. data/test/xml_bindings_test.rb +0 -4
  98. data/test/xml_namespace_test.rb +186 -0
  99. data/test/xml_test.rb +103 -43
  100. data/test/yaml_test.rb +51 -26
  101. metadata +101 -39
  102. data/.travis.yml +0 -7
  103. data/lib/representable/TODO.getting_serious +0 -11
  104. data/lib/representable/autoload.rb +0 -10
  105. data/test/mongoid_test.rb +0 -31
@@ -4,11 +4,12 @@ module Representable
4
4
  class Pipeline < Array
5
5
  Stop = Class.new
6
6
 
7
- # options is mutuable.
7
+ # options is mutable.
8
8
  def call(input, options)
9
9
  inject(input) do |memo, block|
10
10
  res = evaluate(block, memo, options)
11
- return(Stop)if Stop == res
11
+ return(Stop) if Stop == res
12
+
12
13
  res
13
14
  end
14
15
  end
@@ -2,7 +2,8 @@
2
2
  module Representable
3
3
  module Binding::Factories
4
4
  def pipeline_for(name, input, options)
5
- return yield unless proc = @definition[name]
5
+ return yield unless (proc = @definition[name])
6
+
6
7
  # proc.(self, options)
7
8
  instance_exec(input, options, &proc)
8
9
  end
@@ -11,6 +12,7 @@ module Representable
11
12
  def collect_for(item_functions)
12
13
  return [Collect[*item_functions]] if array?
13
14
  return [Collect::Hash[*item_functions]] if self[:hash]
15
+
14
16
  item_functions
15
17
  end
16
18
 
@@ -92,4 +94,4 @@ module Representable
92
94
  funcs << (self[:setter] ? Setter : SetValue)
93
95
  end
94
96
  end
95
- end
97
+ end
@@ -21,7 +21,7 @@ module Representable
21
21
  def self.apply!(options)
22
22
  return unless populator = options[:populator]
23
23
 
24
- options[:parse_pipeline] = ->(input, options) do
24
+ options[:parse_pipeline] = ->(_input, _opts) do
25
25
  pipeline = Pipeline[*parse_functions] # TODO: AssignFragment
26
26
  pipeline = Pipeline::Insert.(pipeline, SetValue, delete: true) # remove the setter function.
27
27
  pipeline = Pipeline::Insert.(pipeline, populator, replace: CreateObject::Populator) # let the actual populator do the job.
@@ -1,6 +1,7 @@
1
1
  module Representable::Represent
2
2
  def represent(represented, array_class=Array)
3
3
  return for_collection.prepare(represented) if represented.is_a?(array_class)
4
+
4
5
  prepare(represented)
5
6
  end
6
7
  end
@@ -3,7 +3,7 @@ module Representable
3
3
  options[:binding].evaluate_option(:getter, input, options)
4
4
  end
5
5
 
6
- GetValue = ->(input, options) { options[:binding].send(:exec_context, options).send(options[:binding].getter) }
6
+ GetValue = ->(_input, options) { options[:binding].send(:exec_context, options).public_send(options[:binding].getter) }
7
7
 
8
8
  Writer = ->(input, options) do
9
9
  options[:binding].evaluate_option(:writer, input, options)
@@ -37,6 +37,7 @@ module Representable
37
37
 
38
38
  Serialize = ->(input, options) do
39
39
  return if input.nil? # DISCUSS: how can we prevent that?
40
+
40
41
  binding, options = options[:binding], options[:options] # FIXME: rename to :local_options.
41
42
 
42
43
  options_for_nested = OptionsForNested.(options, binding)
@@ -51,4 +52,4 @@ module Representable
51
52
  # Warning: don't rely on AssignAs/AssignName, i am not sure if i leave that as functions.
52
53
  AssignAs = ->(input, options) { options[:as] = As.(input, options); input }
53
54
  AssignName = ->(input, options) { options[:as] = options[:binding].name; input }
54
- end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "3.0.0"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -0,0 +1,38 @@
1
+ gem 'virtus'
2
+ require "virtus"
3
+
4
+ module Representable
5
+ module VirtusCoercion
6
+ class Coercer
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ # This gets called when the :render_filter or :parse_filter option is evaluated.
12
+ # Usually the Coercer instance is an element in a Pipeline to allow >1 filters per property.
13
+ def call(input, options)
14
+ Virtus::Attribute.build(@type).coerce(input)
15
+ end
16
+ end
17
+
18
+
19
+ def self.included(base)
20
+ base.class_eval do
21
+ extend ClassMethods
22
+ register_feature VirtusCoercion
23
+ end
24
+ end
25
+
26
+
27
+ module ClassMethods
28
+ def property(name, options={}, &block)
29
+ super.tap do |definition|
30
+ return definition unless type = options[:type]
31
+
32
+ definition.merge!(render_filter: coercer = Coercer.new(type))
33
+ definition.merge!(parse_filter: coercer)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,15 +1,14 @@
1
- require 'representable'
2
- require 'representable/xml/binding'
3
- require 'representable/xml/collection'
1
+ gem 'nokogiri', '> 1.10.8'
2
+ require 'nokogiri'
4
3
 
5
- begin
6
- require 'nokogiri'
7
- rescue LoadError => _
8
- abort "Missing dependency 'nokogiri' for Representable::XML. See dependencies section in README.md for details."
9
- end
4
+ require 'representable'
10
5
 
11
6
  module Representable
12
7
  module XML
8
+ autoload :Binding, 'representable/xml/binding'
9
+ autoload :Collection, 'representable/xml/collection'
10
+ autoload :Namespace, 'representable/xml/namespace'
11
+
13
12
  def self.included(base)
14
13
  base.class_eval do
15
14
  include Representable
@@ -46,16 +45,19 @@ module Representable
46
45
 
47
46
  # Returns a Nokogiri::XML object representing this object.
48
47
  def to_node(options={})
49
- options[:doc] ||= Nokogiri::XML::Document.new
48
+ options[:doc] = Nokogiri::XML::Document.new # DISCUSS: why do we need a fresh Document here?
50
49
  root_tag = options[:wrap] || representation_wrap(options)
51
50
 
52
- create_representation_with(Nokogiri::XML::Node.new(root_tag.to_s, options[:doc]), options, Binding)
51
+ create_representation_with(Node(options[:doc], root_tag.to_s), options, Binding)
53
52
  end
54
53
 
55
54
  def to_xml(*args)
56
55
  to_node(*args).to_s
57
56
  end
58
57
 
58
+ alias_method :render, :to_xml
59
+ alias_method :parse, :from_xml
60
+
59
61
  private
60
62
  def remove_namespaces?
61
63
  # TODO: make local Config easily extendable so you get Config#remove_ns? etc.
@@ -1,8 +1,14 @@
1
1
  require 'representable/binding'
2
- require 'representable/hash/binding.rb'
3
2
 
4
3
  module Representable
5
4
  module XML
5
+ module_function def Node(document, name, attributes={})
6
+ node = Nokogiri::XML::Node.new(name.to_s, document) # Java::OrgW3cDom::DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
7
+
8
+ attributes.each { |k, v| node[k] = v } # TODO: benchmark.
9
+ node
10
+ end
11
+
6
12
  class Binding < Representable::Binding
7
13
  def self.build_for(definition)
8
14
  return Collection.new(definition) if definition.array?
@@ -10,6 +16,7 @@ module Representable
10
16
  return AttributeHash.new(definition) if definition.hash? and definition[:use_attributes]
11
17
  return Attribute.new(definition) if definition[:attribute]
12
18
  return Content.new(definition) if definition[:content]
19
+
13
20
  new(definition)
14
21
  end
15
22
 
@@ -17,7 +24,7 @@ module Representable
17
24
  wrap_node = parent
18
25
 
19
26
  if wrap = self[:wrap]
20
- parent << wrap_node = node_for(parent, wrap)
27
+ parent << wrap_node = XML::Node(parent, wrap)
21
28
  end
22
29
 
23
30
  wrap_node << serialize_for(fragments, parent, as)
@@ -32,12 +39,15 @@ module Representable
32
39
 
33
40
  # Creates wrapped node for the property.
34
41
  def serialize_for(value, parent, as)
35
- node = node_for(parent, as)
36
- serialize_node(node, value)
42
+ node = XML::Node(parent, as) # node doesn't have attr="" attributes!!!
43
+ serialize_node(node, value, as)
37
44
  end
38
45
 
39
- def serialize_node(node, value)
40
- return value if typed?
46
+ def serialize_node(node, value, as)
47
+ if typed?
48
+ value.name = as if as != self[:name]
49
+ return value
50
+ end
41
51
 
42
52
  node.content = value
43
53
  node
@@ -60,11 +70,7 @@ module Representable
60
70
  def find_nodes(doc, as)
61
71
  selector = as
62
72
  selector = "#{self[:wrap]}/#{as}" if self[:wrap]
63
- nodes = doc.xpath(selector)
64
- end
65
-
66
- def node_for(parent, name)
67
- Nokogiri::XML::Node.new(name.to_s, parent.document)
73
+ doc.xpath(selector) # nodes
68
74
  end
69
75
 
70
76
  def content_for(node) # TODO: move this into a ScalarDecorator.
@@ -100,8 +106,8 @@ module Representable
100
106
  class Hash < Collection
101
107
  def serialize_for(value, parent, as)
102
108
  set_for(parent, value.collect do |k, v|
103
- node = node_for(parent, k)
104
- serialize_node(node, v)
109
+ node = XML::Node(parent, k)
110
+ serialize_node(node, v, as)
105
111
  end)
106
112
  end
107
113
 
@@ -0,0 +1,122 @@
1
+ module Representable::XML
2
+ # Experimental!
3
+ # Best explanation so far: http://books.xmlschemata.org/relaxng/relax-CHP-11-SECT-1.html
4
+ #
5
+ # Note: This module doesn't work with JRuby because Nokogiri uses a completely
6
+ # different implementation in Java which has other requirements that we couldn't fulfil.
7
+ # Please wait for Representable 4 where we replace Nokogiri with Oga.
8
+ module Namespace
9
+ def self.included(includer)
10
+ includer.extend(DSL)
11
+ end
12
+
13
+ module DSL
14
+ def namespace(namespace)
15
+ representable_attrs.options[:local_namespace] = namespace
16
+ representable_attrs.options[:namespace_mappings] ||= {}
17
+ representable_attrs.options[:namespace_mappings][namespace] = nil # this might get overwritten via #namespace_def later.
18
+ end
19
+
20
+ def namespace_def(mapping)
21
+ namespace_defs.merge!(mapping.invert)
22
+ end
23
+
24
+ # :private:
25
+ def namespace_defs
26
+ representable_attrs.options[:namespace_mappings] ||= {}
27
+ end
28
+
29
+ def property(name, options={})
30
+ uri = representable_attrs.options[:local_namespace] # per default, a property belongs to the local namespace.
31
+ options[:namespace] ||= uri # don't override if already set.
32
+
33
+ # a nested representer is automatically assigned "its" local namespace. It's like saying
34
+ # property :author, namespace: "http://ns/author" do ... end
35
+
36
+ super.tap do |dfn|
37
+ if dfn.typed? # FIXME: ouch, this should be doable with property's API to hook into the creation process.
38
+ dfn.merge!( namespace: dfn.representer_module.representable_attrs.options[:local_namespace] )
39
+
40
+ update_namespace_defs!(namespace_defs)
41
+ end
42
+ end
43
+ end
44
+
45
+ # :private:
46
+ # super ugly hack
47
+ # recursively injects the namespace_defs into all representers of this tree. will be done better in 4.0.
48
+ def update_namespace_defs!(namespace_defs)
49
+ representable_attrs.each do |dfn|
50
+ dfn.merge!(namespace_defs: namespace_defs) # this only helps with scalars
51
+
52
+ if dfn.typed?
53
+ representer = Class.new(dfn.representer_module) # don't pollute classes.
54
+ representer.update_namespace_defs!(namespace_defs)
55
+ dfn.merge!(extend: representer)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ module AsWithNamespace
62
+ def write(doc, fragment, as)
63
+ super(doc, fragment, prefixed(self, as))
64
+ end
65
+
66
+ # FIXME: this is shit, the NestedOptions is executed too late here!
67
+ def read(node, as)
68
+ super(node, prefixed(self, as))
69
+ end
70
+
71
+ private
72
+ def prefixed(dfn, as)
73
+ uri = dfn[:namespace] # this is generic behavior and per property
74
+ prefix = dfn[:namespace_defs][uri]
75
+ as = Namespace::Namespaced(prefix, as)
76
+ end
77
+ end
78
+
79
+ # FIXME: some "bug" in Representable's XML doesn't consider the container tag, so we could theoretically pick the
80
+ # wrong namespaced tag here :O
81
+ def from_node(node, options={})
82
+ super
83
+ end
84
+
85
+ def to_node(options={})
86
+ local_uri = representable_attrs.options[:local_namespace] # every decorator MUST have a local namespace.
87
+ prefix = self.class.namespace_defs[local_uri]
88
+
89
+ root_tag = [prefix, representation_wrap(options)].compact.join(":")
90
+
91
+ options = { wrap: root_tag }.merge(options)
92
+
93
+ # TODO: there should be an easier way to pass a set of options to all nested #to_node decorators.
94
+ representable_attrs.keys.each do |property|
95
+ options[property.to_sym] = { show_definition: false, namespaces: options[:namespaces] }
96
+ end
97
+
98
+ super(options).tap do |node|
99
+ add_namespace_definitions!(node, self.class.namespace_defs) unless options[:show_definition] == false
100
+ end
101
+ end
102
+
103
+ # "Physically" add `xmlns` attributes to `node`.
104
+ def add_namespace_definitions!(node, namespaces)
105
+ namespaces.each do |uri, prefix|
106
+ prefix = prefix.nil? ? nil : prefix.to_s
107
+ node.add_namespace_definition(prefix, uri)
108
+ end
109
+ end
110
+
111
+ def self.Namespaced(prefix, name)
112
+ [ prefix, name ].compact.join(":")
113
+ end
114
+
115
+ # FIXME: this is a PoC, we need a better API to inject code.
116
+ def representable_map(options, format)
117
+ super.tap do |map|
118
+ map.each { |bin| bin.extend(AsWithNamespace) unless bin.is_a?(Binding::Attribute) }
119
+ end
120
+ end
121
+ end
122
+ end
@@ -1,8 +1,9 @@
1
- require 'representable/hash'
2
- require 'representable/yaml/binding'
1
+ require 'psych'
2
+ require 'representable'
3
3
 
4
4
  module Representable
5
5
  module YAML
6
+ autoload :Binding, 'representable/yaml/binding'
6
7
  include Hash
7
8
 
8
9
  def self.included(base)
@@ -38,5 +39,8 @@ module Representable
38
39
  doc.children << to_ast(*args)
39
40
  stream.to_yaml
40
41
  end
42
+
43
+ alias_method :render, :to_yaml
44
+ alias_method :parse, :from_yaml
41
45
  end
42
46
  end
@@ -5,6 +5,7 @@ module Representable
5
5
  class Binding < Representable::Hash::Binding
6
6
  def self.build_for(definition)
7
7
  return Collection.new(definition) if definition.array?
8
+
8
9
  new(definition)
9
10
  end
10
11
 
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.platform = Gem::Platform::RUBY
10
10
  spec.authors = ["Nick Sutterer"]
11
11
  spec.email = ["apotonick@gmail.com"]
12
- spec.homepage = "https://github.com/apotonick/representable/"
12
+ spec.homepage = "https://github.com/trailblazer/representable/"
13
13
  spec.summary = %q{Renders and parses JSON/XML/YAML documents from and to Ruby objects. Includes plain properties, collections, nesting, coercion and more.}
14
14
  spec.description = spec.summary
15
15
 
@@ -19,17 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
  spec.license = "MIT"
21
21
 
22
- spec.required_ruby_version = '>= 1.9.3'
22
+ spec.required_ruby_version = '>= 2.4.0'
23
23
 
24
- spec.add_dependency "uber", "~> 0.0.15"
25
- spec.add_dependency "declarative", "~> 0.0.5"
24
+ spec.add_dependency "uber", "< 0.2.0"
25
+ spec.add_dependency "declarative", "< 0.1.0"
26
+ spec.add_dependency "trailblazer-option", "~> 0.1.0"
26
27
 
27
28
  spec.add_development_dependency "rake"
28
- spec.add_development_dependency "test_xml", "0.1.6"
29
+ spec.add_development_dependency "test_xml", ">= 0.1.6"
29
30
  spec.add_development_dependency "minitest"
30
- spec.add_development_dependency "mongoid"
31
31
  spec.add_development_dependency "virtus"
32
- spec.add_development_dependency "json", '>= 1.7.7'
33
-
34
- spec.add_development_dependency "ruby-prof"
32
+ spec.add_development_dependency "dry-types"
33
+ spec.add_development_dependency "ruby-prof" if RUBY_ENGINE == "ruby" # mri
35
34
  end
data/test/as_test.rb CHANGED
@@ -7,8 +7,8 @@ class AsTest < MiniTest::Spec
7
7
  # :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
8
8
  ) do |format, mod, input, output|
9
9
 
10
- let (:song) { representer.prepare(Song.new("Revolution")) }
11
- let (:format) { format }
10
+ let(:song) { representer.prepare(Song.new("Revolution")) }
11
+ let(:format) { format }
12
12
 
13
13
 
14
14
  describe "as: with :symbol" do
@@ -17,7 +17,7 @@ class AsTest < MiniTest::Spec
17
17
  end
18
18
 
19
19
  it { render(song).must_equal_document output }
20
- it { parse(song, input).name.must_equal "Wie Es Geht" }
20
+ it { _(parse(song, input).name).must_equal "Wie Es Geht" }
21
21
  end
22
22
 
23
23
 
@@ -27,7 +27,7 @@ class AsTest < MiniTest::Spec
27
27
  end
28
28
 
29
29
  it { render(song).must_equal_document({"Song" => "Revolution"}) }
30
- it { parse(song, {"Song" => "Wie Es Geht"}).name.must_equal "Wie Es Geht" }
30
+ it { _(parse(song, {"Song" => "Wie Es Geht"}).name).must_equal "Wie Es Geht" }
31
31
  end
32
32
 
33
33
 
@@ -37,7 +37,7 @@ class AsTest < MiniTest::Spec
37
37
  end
38
38
 
39
39
  it { render(song, user_options:{volume: 1}).must_equal_document({"{:volume=>1}" => "Revolution"}) }
40
- it { parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name.must_equal "Wie Es Geht" }
40
+ it { _(parse(song, {"{:volume=>1}" => "Wie Es Geht"}, user_options: {volume: 1}).name).must_equal "Wie Es Geht" }
41
41
  end
42
42
  end
43
43
  end
@@ -60,6 +60,6 @@ class AsXmlTest < MiniTest::Spec
60
60
 
61
61
  it do
62
62
  skip
63
- representer.new(Album.new(Band.new("Offspring"))).to_xml.must_equal ""
63
+ _(representer.new(Album.new(Band.new("Offspring"))).to_xml).must_equal ""
64
64
  end
65
- end
65
+ end