File: binding.rb

package info (click to toggle)
ruby-representable 3.0.4-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 896 kB
  • sloc: ruby: 6,432; makefile: 3
file content (172 lines) | stat: -rw-r--r-- 4,539 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
require 'representable/binding'
require 'representable/hash/binding.rb'

module Representable
  module XML
    module_function
    def Node(document, name, attributes={})
      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.

      attributes.each { |k, v| node[k] = v } # TODO: benchmark.
      node
    end

    class Binding < Representable::Binding
      def self.build_for(definition)
        return Collection.new(definition)      if definition.array?
        return Hash.new(definition)            if definition.hash? and not definition[:use_attributes] # FIXME: hate this.
        return AttributeHash.new(definition)   if definition.hash? and definition[:use_attributes]
        return Attribute.new(definition)       if definition[:attribute]
        return Content.new(definition)         if definition[:content]
        new(definition)
      end

      def write(parent, fragments, as)
        wrap_node = parent

        if wrap = self[:wrap]
          parent << wrap_node = XML::Node(parent, wrap)
        end

        wrap_node << serialize_for(fragments, parent, as)
      end

      def read(node, as)
        nodes = find_nodes(node, as)
        return FragmentNotFound if nodes.size == 0 # TODO: write dedicated test!

        deserialize_from(nodes)
      end

      # Creates wrapped node for the property.
      def serialize_for(value, parent, as)
        node = XML::Node(parent, as) # node doesn't have attr="" attributes!!!
        serialize_node(node, value, as)
      end

      def serialize_node(node, value, as)
        if typed?
          value.name = as if as != self[:name]
          return value
        end

        node.content = value
        node
      end

      def deserialize_from(nodes)
        content_for(nodes.first)
      end

      # DISCUSS: why is this public?
      def serialize_method
        :to_node
      end

      def deserialize_method
        :from_node
      end

    private
      def find_nodes(doc, as)
        selector  = as
        selector  = "#{self[:wrap]}/#{as}" if self[:wrap]
        doc.xpath(selector) # nodes
      end

      def content_for(node) # TODO: move this into a ScalarDecorator.
        return node if typed?

        node.content
      end


      class Collection < self
        include Representable::Binding::Collection

        def serialize_for(value, parent, as)
          # return NodeSet so << works.
          set_for(parent, value.collect { |item| super(item, parent, as) })
        end

        def deserialize_from(nodes)
          content_nodes = nodes.collect do |item| # TODO: move this to Node?
            content_for(item)
          end

          content_nodes
        end

      private
        def set_for(parent, nodes)
          Nokogiri::XML::NodeSet.new(parent.document, nodes)
        end
      end


      class Hash < Collection
        def serialize_for(value, parent, as)
          set_for(parent, value.collect do |k, v|
            node = XML::Node(parent, k)
            serialize_node(node, v, as)
          end)
        end

        def deserialize_from(nodes)
          hash = {}
          nodes.children.each do |node|
            hash[node.name] = content_for node
          end

          hash
        end
      end

      class AttributeHash < Collection
        # DISCUSS: use AttributeBinding here?
        def write(parent, value, as)  # DISCUSS: is it correct overriding #write here?
          value.collect do |k, v|
            parent[k] = v.to_s
          end
          parent
        end

        # FIXME: this is not tested!
        def deserialize_from(node)
          HashDeserializer.new(self).deserialize(node)
        end
      end


      # Represents a tag attribute. Currently this only works on the top-level tag.
      class Attribute < self
        def read(node, as)
          node[as]
        end

        def serialize_for(value, parent, as)
          parent[as] = value.to_s
        end

        def write(parent, value, as)
          serialize_for(value, parent, as)
        end
      end

      # Represents tag content.
      class Content < self
        def read(node, as)
          node.content
        end

        def serialize_for(value, parent)
          parent.content = value.to_s
        end

        def write(parent, value, as)
          serialize_for(value, parent)
        end
      end
    end # Binding
  end
end