opal-browser 0.1.0.beta1

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 (127) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +11 -0
  5. data/README.md +106 -0
  6. data/Rakefile +5 -0
  7. data/config.ru +63 -0
  8. data/lib/opal/browser.rb +4 -0
  9. data/opal-browser.gemspec +23 -0
  10. data/opal/browser.rb +10 -0
  11. data/opal/browser/animation_frame.rb +29 -0
  12. data/opal/browser/canvas.rb +277 -0
  13. data/opal/browser/canvas/data.rb +73 -0
  14. data/opal/browser/canvas/gradient.rb +37 -0
  15. data/opal/browser/canvas/style.rb +123 -0
  16. data/opal/browser/canvas/text.rb +55 -0
  17. data/opal/browser/compatibility.rb +59 -0
  18. data/opal/browser/compatibility/animation_frame.rb +93 -0
  19. data/opal/browser/compatibility/dom/document/window.rb +15 -0
  20. data/opal/browser/compatibility/dom/element/css.rb +15 -0
  21. data/opal/browser/compatibility/dom/element/matches.rb +31 -0
  22. data/opal/browser/compatibility/dom/element/offset.rb +20 -0
  23. data/opal/browser/compatibility/dom/element/scroll.rb +25 -0
  24. data/opal/browser/compatibility/dom/element/style.rb +15 -0
  25. data/opal/browser/compatibility/dom/mutation_observer.rb +47 -0
  26. data/opal/browser/compatibility/http/request.rb +15 -0
  27. data/opal/browser/compatibility/immediate.rb +107 -0
  28. data/opal/browser/compatibility/window/scroll.rb +27 -0
  29. data/opal/browser/compatibility/window/size.rb +13 -0
  30. data/opal/browser/compatibility/window/view.rb +13 -0
  31. data/opal/browser/console.rb +137 -0
  32. data/opal/browser/cookies.rb +79 -0
  33. data/opal/browser/css.rb +24 -0
  34. data/opal/browser/css/declaration.rb +88 -0
  35. data/opal/browser/css/rule.rb +48 -0
  36. data/opal/browser/css/rule/style.rb +16 -0
  37. data/opal/browser/css/style_sheet.rb +83 -0
  38. data/opal/browser/css/unit.rb +188 -0
  39. data/opal/browser/dom.rb +95 -0
  40. data/opal/browser/dom/attribute.rb +19 -0
  41. data/opal/browser/dom/builder.rb +97 -0
  42. data/opal/browser/dom/cdata.rb +9 -0
  43. data/opal/browser/dom/character_data.rb +37 -0
  44. data/opal/browser/dom/comment.rb +9 -0
  45. data/opal/browser/dom/compatibility.rb +8 -0
  46. data/opal/browser/dom/document.rb +83 -0
  47. data/opal/browser/dom/document_fragment.rb +7 -0
  48. data/opal/browser/dom/element.rb +290 -0
  49. data/opal/browser/dom/element/input.rb +17 -0
  50. data/opal/browser/dom/element/offset.rb +67 -0
  51. data/opal/browser/dom/element/position.rb +37 -0
  52. data/opal/browser/dom/element/scroll.rb +49 -0
  53. data/opal/browser/dom/event.rb +240 -0
  54. data/opal/browser/dom/event/animation.rb +26 -0
  55. data/opal/browser/dom/event/audio_processing.rb +31 -0
  56. data/opal/browser/dom/event/base.rb +207 -0
  57. data/opal/browser/dom/event/before_unload.rb +13 -0
  58. data/opal/browser/dom/event/clipboard.rb +26 -0
  59. data/opal/browser/dom/event/close.rb +35 -0
  60. data/opal/browser/dom/event/composition.rb +38 -0
  61. data/opal/browser/dom/event/custom.rb +30 -0
  62. data/opal/browser/dom/event/device_light.rb +21 -0
  63. data/opal/browser/dom/event/device_motion.rb +38 -0
  64. data/opal/browser/dom/event/device_orientation.rb +36 -0
  65. data/opal/browser/dom/event/device_proximity.rb +31 -0
  66. data/opal/browser/dom/event/drag.rb +113 -0
  67. data/opal/browser/dom/event/focus.rb +23 -0
  68. data/opal/browser/dom/event/gamepad.rb +47 -0
  69. data/opal/browser/dom/event/hash_change.rb +26 -0
  70. data/opal/browser/dom/event/keyboard.rb +93 -0
  71. data/opal/browser/dom/event/message.rb +50 -0
  72. data/opal/browser/dom/event/mouse.rb +253 -0
  73. data/opal/browser/dom/event/page_transition.rb +21 -0
  74. data/opal/browser/dom/event/pop_state.rb +21 -0
  75. data/opal/browser/dom/event/progress.rb +31 -0
  76. data/opal/browser/dom/event/sensor.rb +13 -0
  77. data/opal/browser/dom/event/storage.rb +41 -0
  78. data/opal/browser/dom/event/touch.rb +69 -0
  79. data/opal/browser/dom/event/ui.rb +22 -0
  80. data/opal/browser/dom/event/wheel.rb +49 -0
  81. data/opal/browser/dom/mutation_observer.rb +118 -0
  82. data/opal/browser/dom/node.rb +317 -0
  83. data/opal/browser/dom/node_set.rb +88 -0
  84. data/opal/browser/dom/text.rb +21 -0
  85. data/opal/browser/effects.rb +39 -0
  86. data/opal/browser/event_source.rb +67 -0
  87. data/opal/browser/history.rb +54 -0
  88. data/opal/browser/http.rb +129 -0
  89. data/opal/browser/http/binary.rb +57 -0
  90. data/opal/browser/http/compatibility.rb +1 -0
  91. data/opal/browser/http/headers.rb +90 -0
  92. data/opal/browser/http/parameters.rb +8 -0
  93. data/opal/browser/http/request.rb +331 -0
  94. data/opal/browser/http/response.rb +115 -0
  95. data/opal/browser/immediate.rb +43 -0
  96. data/opal/browser/interval.rb +93 -0
  97. data/opal/browser/location.rb +77 -0
  98. data/opal/browser/navigator.rb +151 -0
  99. data/opal/browser/screen.rb +40 -0
  100. data/opal/browser/socket.rb +115 -0
  101. data/opal/browser/storage.rb +149 -0
  102. data/opal/browser/timeout.rb +60 -0
  103. data/opal/browser/utils.rb +56 -0
  104. data/opal/browser/version.rb +3 -0
  105. data/opal/browser/window.rb +113 -0
  106. data/opal/browser/window/compatibility.rb +3 -0
  107. data/opal/browser/window/scroll.rb +49 -0
  108. data/opal/browser/window/size.rb +35 -0
  109. data/opal/browser/window/view.rb +18 -0
  110. data/spec/dom/builder_spec.rb +69 -0
  111. data/spec/dom/document_spec.rb +40 -0
  112. data/spec/dom/element_spec.rb +46 -0
  113. data/spec/dom/event_spec.rb +127 -0
  114. data/spec/dom/mutation_observer_spec.rb +37 -0
  115. data/spec/dom/node_spec.rb +154 -0
  116. data/spec/dom_spec.rb +13 -0
  117. data/spec/event_source_spec.rb +42 -0
  118. data/spec/history_spec.rb +48 -0
  119. data/spec/http_spec.rb +87 -0
  120. data/spec/immediate_spec.rb +12 -0
  121. data/spec/json2.js +486 -0
  122. data/spec/sizzle.js +5 -0
  123. data/spec/socket_spec.rb +43 -0
  124. data/spec/spec_helper.rb +37 -0
  125. data/spec/storage_spec.rb +26 -0
  126. data/spec/window_spec.rb +10 -0
  127. metadata +240 -0
@@ -0,0 +1,95 @@
1
+ require 'browser/dom/event'
2
+ require 'browser/dom/node_set'
3
+ require 'browser/dom/node'
4
+ require 'browser/dom/attribute'
5
+ require 'browser/dom/character_data'
6
+ require 'browser/dom/text'
7
+ require 'browser/dom/cdata'
8
+ require 'browser/dom/comment'
9
+ require 'browser/dom/element'
10
+ require 'browser/dom/document'
11
+ require 'browser/dom/document_fragment'
12
+ require 'browser/dom/builder'
13
+ require 'browser/dom/mutation_observer'
14
+
15
+ require 'browser/dom/compatibility'
16
+
17
+ module Kernel
18
+ # Parse an XML string into a DOM usable {Browser::DOM::Document}
19
+ #
20
+ # @param what [String] the string to parse
21
+ # @return [Browser::DOM::Document] the document
22
+ def XML(what)
23
+ %x{
24
+ var doc;
25
+
26
+ if (window.DOMParser) {
27
+ doc = new DOMParser().parseFromString(what, 'text/xml');
28
+ }
29
+ else {
30
+ doc = new ActiveXObject('Microsoft.XMLDOM');
31
+ doc.async = 'false';
32
+ doc.loadXML(what);
33
+ }
34
+ }
35
+
36
+ DOM(`doc`)
37
+ end
38
+
39
+ # Wrap a native element or create a DOM tree using the {Paggio::HTML} DSL.
40
+ #
41
+ # @return [Browser::DOM::Node]
42
+ def DOM(*args, &block)
43
+ if block
44
+ document = args.shift || $document
45
+ element = args.shift
46
+
47
+ roots = Browser::DOM::Builder.new(document, element, &block).to_a
48
+
49
+ if roots.length == 1
50
+ roots.first
51
+ else
52
+ Browser::DOM::NodeSet.new(document, roots)
53
+ end
54
+ else
55
+ what = args.shift
56
+ document = args.shift || $document
57
+
58
+ if native?(what)
59
+ Browser::DOM::Node.new(what)
60
+ elsif Browser::DOM::Node === what
61
+ what
62
+ elsif String === what
63
+ %x{
64
+ var doc = #{Native.try_convert(document)}.createElement('div');
65
+ doc.innerHTML = what;
66
+
67
+ return #{DOM(`doc.childNodes.length == 1 ? doc.childNodes[0] : doc`)};
68
+ }
69
+ else
70
+ raise ArgumentError, "argument not DOM convertible"
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ module Browser
77
+
78
+ class Window
79
+ include DOM::Event::Target
80
+
81
+ target {|value|
82
+ $window if `#{value} == window`
83
+ }
84
+
85
+ # Get the {DOM::Document} for this window.
86
+ #
87
+ # @return [DOM::Document]
88
+ def document
89
+ DOM(`#@native.document`)
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ $document = $window.document
@@ -0,0 +1,19 @@
1
+ module Browser; module DOM
2
+
3
+ class Attribute
4
+ include Native
5
+
6
+ def id?
7
+ `#@native.isId`
8
+ end
9
+
10
+ def name
11
+ `#@native.name`
12
+ end
13
+
14
+ def value
15
+ `#@native.value`
16
+ end
17
+ end
18
+
19
+ end; end
@@ -0,0 +1,97 @@
1
+ # Temporary fix for a bug in String#scan
2
+ module Paggio::Utils
3
+ def self.heredoc(string)
4
+ string
5
+ end
6
+ end
7
+
8
+ class Paggio::HTML::Element < BasicObject
9
+ def on(*args, &block)
10
+ (@on ||= []) << [args, block]
11
+ end
12
+
13
+ def style(*args, &block)
14
+ @style = [args, block]
15
+ end
16
+ end
17
+
18
+ module Browser; module DOM
19
+
20
+ class Builder
21
+ def self.to_h
22
+ @builders ||= {}
23
+ end
24
+
25
+ def self.for(klass, &block)
26
+ if block
27
+ to_h[klass] = block
28
+ else
29
+ to_h[klass]
30
+ end
31
+ end
32
+
33
+ def self.build(builder, item)
34
+ to_h.each {|klass, block|
35
+ if klass === item
36
+ break block.call(builder, item)
37
+ end
38
+ }
39
+ end
40
+
41
+ attr_reader :document, :element
42
+
43
+ def initialize(document, element = nil, &block)
44
+ @document = document
45
+ @element = element
46
+ @builder = Paggio::HTML.new(&block)
47
+ @roots = @builder.each.map { |e| Builder.build(self, e) }
48
+
49
+ if @element
50
+ @roots.each {|root|
51
+ @element << root
52
+ }
53
+ end
54
+ end
55
+
56
+ def to_a
57
+ @roots
58
+ end
59
+ end
60
+
61
+ Builder.for String do |b, item|
62
+ b.document.create_text(item)
63
+ end
64
+
65
+ Builder.for Paggio::HTML::Element do |b, item|
66
+ dom = b.document.create_element(`item.name`)
67
+
68
+ if Hash === `item.attributes`
69
+ dom.attributes.merge!(`item.attributes`)
70
+ end
71
+
72
+ `item.class_names`.each {|value|
73
+ dom.add_class value
74
+ }
75
+
76
+ if on = `item.on || nil`
77
+ on.each {|args, block|
78
+ dom.on(*args, &block)
79
+ }
80
+ end
81
+
82
+ if style = `item.style || nil`
83
+ dom.style(*style[0], &style[1])
84
+ end
85
+
86
+ if inner = `item.inner_html || nil`
87
+ dom.inner_html = inner
88
+ else
89
+ item.each {|child|
90
+ dom << Builder.build(b, child)
91
+ }
92
+ end
93
+
94
+ dom
95
+ end
96
+
97
+ end; end
@@ -0,0 +1,9 @@
1
+ module Browser; module DOM
2
+
3
+ class CDATA < Text
4
+ def inspect
5
+ "#<DOM::CDATA: #{value}>"
6
+ end
7
+ end
8
+
9
+ end; end
@@ -0,0 +1,37 @@
1
+ module Browser; module DOM
2
+
3
+ class CharacterData < Node
4
+ def data
5
+ `#@native.data`
6
+ end
7
+
8
+ def append(string)
9
+ `#@native.appendData(string)`
10
+
11
+ self
12
+ end
13
+
14
+ def insert(string, offset = 0)
15
+ `#@native.insertData(offset, string)`
16
+
17
+ self
18
+ end
19
+
20
+ def delete(count, offset = 0)
21
+ `#@native.deleteData(offset, count)`
22
+
23
+ self
24
+ end
25
+
26
+ def replace(string, offset = 0, count = `#@native.length`)
27
+ `#@native.replaceData(offset, count, string)`
28
+
29
+ self
30
+ end
31
+
32
+ def substring(count, offset = 0)
33
+ `#@native.substringData(offset, count)`
34
+ end
35
+ end
36
+
37
+ end; end
@@ -0,0 +1,9 @@
1
+ module Browser; module DOM
2
+
3
+ class Comment < CharacterData
4
+ def inspect
5
+ "#<DOM::Comment: #{value}>"
6
+ end
7
+ end
8
+
9
+ end; end
@@ -0,0 +1,8 @@
1
+ require 'browser/compatibility/dom/document/window'
2
+
3
+ require 'browser/compatibility/dom/mutation_observer'
4
+
5
+ require 'browser/compatibility/dom/element/matches'
6
+ require 'browser/compatibility/dom/element/css'
7
+ require 'browser/compatibility/dom/element/offset'
8
+ require 'browser/compatibility/dom/element/style'
@@ -0,0 +1,83 @@
1
+ require 'browser/location'
2
+
3
+ module Browser; module DOM
4
+
5
+ class Document < Element
6
+ def create_element(name, options = {})
7
+ if ns = options[:namespace]
8
+ DOM(`#@native.createElementNS(#{ns}, #{name})`)
9
+ else
10
+ DOM(`#@native.createElement(name)`)
11
+ end
12
+ end
13
+
14
+ def window
15
+ Window.new(`#@native.defaultView`)
16
+ end
17
+
18
+ def create_text(content)
19
+ DOM(`#@native.createTextNode(#{content})`)
20
+ end
21
+
22
+ def [](what)
23
+ %x{
24
+ var result = #@native.getElementById(what);
25
+
26
+ if (result) {
27
+ return #{DOM(`result`)};
28
+ }
29
+ }
30
+
31
+ xpath(what).first || css(what).first
32
+ end
33
+
34
+ alias at []
35
+
36
+ def cookies
37
+ Cookies.new(@native) if defined?(`#@native.cookie`)
38
+ end
39
+
40
+ def document
41
+ self
42
+ end
43
+
44
+ def inspect
45
+ "#<DOM::Document: #{children.inspect}>"
46
+ end
47
+
48
+ def location
49
+ Location.new(`#@native.location`) if `#@native.location`
50
+ end
51
+
52
+ def title
53
+ `#@native.title`
54
+ end
55
+
56
+ def title=(value)
57
+ `#@native.title = value`
58
+ end
59
+
60
+ def root
61
+ DOM(`#@native.documentElement`)
62
+ end
63
+
64
+ def head
65
+ DOM(`#@native.getElementsByTagName("head")[0]`)
66
+ end
67
+
68
+ def body
69
+ DOM(`#@native.body`)
70
+ end
71
+
72
+ def style_sheets
73
+ Native::Array.new(`#@native.styleSheets`) {|e|
74
+ CSS::StyleSheet.new(e)
75
+ }
76
+ end
77
+
78
+ def root=(element)
79
+ `#@native.documentElement = #{Native.convert(element)}`
80
+ end
81
+ end
82
+
83
+ end; end
@@ -0,0 +1,7 @@
1
+ module Browser; module DOM
2
+
3
+ class DocumentFragment < Element
4
+
5
+ end
6
+
7
+ end; end
@@ -0,0 +1,290 @@
1
+ require 'browser/dom/element/position'
2
+ require 'browser/dom/element/offset'
3
+ require 'browser/dom/element/scroll'
4
+
5
+ require 'browser/dom/element/input'
6
+
7
+ module Browser; module DOM
8
+
9
+ class Element < Node
10
+ def self.create(*args)
11
+ $document.create_element(*args)
12
+ end
13
+
14
+ def self.new(node)
15
+ if self == Element
16
+ case `node.nodeName`.downcase
17
+ when :input
18
+ Input.new(node)
19
+
20
+ else super
21
+ end
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ include Event::Target
28
+
29
+ target {|value|
30
+ DOM(value) rescue nil
31
+ }
32
+
33
+ alias_native :id
34
+
35
+ def add_class(*names)
36
+ `#@native.className = #{(class_names + names).uniq.join ' '}`
37
+
38
+ self
39
+ end
40
+
41
+ def remove_class(*names)
42
+ `#@native.className = #{(class_names - names).join ' '}`
43
+
44
+ self
45
+ end
46
+
47
+ alias_native :class_name, :className
48
+
49
+ def class_names
50
+ `#@native.className`.split(/\s+/).reject(&:empty?)
51
+ end
52
+
53
+ alias attribute attr
54
+
55
+ def attribute_nodes
56
+ Native::Array.new(`#@native.attributes`, get: :item) { |e| DOM(e) }
57
+ end
58
+
59
+ def attributes(options = {})
60
+ Attributes.new(self, options)
61
+ end
62
+
63
+ def get(name, options = {})
64
+ if namespace = options[:namespace]
65
+ `#@native.getAttributeNS(#{namespace.to_s}, #{name.to_s}) || nil`
66
+ else
67
+ `#@native.getAttribute(#{name.to_s}) || nil`
68
+ end
69
+ end
70
+
71
+ def set(name, value, options = {})
72
+ if namespace = options[:namespace]
73
+ `#@native.setAttributeNS(#{namespace.to_s}, #{name.to_s}, #{value})`
74
+ else
75
+ `#@native.setAttribute(#{name.to_s}, #{value.to_s})`
76
+ end
77
+ end
78
+
79
+ alias [] get
80
+ alias []= set
81
+
82
+ alias attr get
83
+ alias attribute get
84
+
85
+ alias get_attribute get
86
+ alias set_attribute set
87
+
88
+ def key?(name)
89
+ !!self[name]
90
+ end
91
+
92
+ def keys
93
+ attributes_nodesmap(&:name)
94
+ end
95
+
96
+ def values
97
+ attribute_nodes.map(&:value)
98
+ end
99
+
100
+ def remove_attribute(name)
101
+ `#@native.removeAttribute(name)`
102
+ end
103
+
104
+ def size(*inc)
105
+ Size.new(`#@native.offsetWidth`, `#@native.offsetHeight`)
106
+ end
107
+
108
+ def position
109
+ Position.new(self)
110
+ end
111
+
112
+ def offset(*values)
113
+ off = Offset.new(self)
114
+
115
+ unless values.empty?
116
+ off.set(*values)
117
+ end
118
+
119
+ off
120
+ end
121
+
122
+ def offset=(value)
123
+ offset.set(*value)
124
+ end
125
+
126
+ def scroll
127
+ Scroll.new(self)
128
+ end
129
+
130
+ def inner_dom(&block)
131
+ # FIXME: when block passing is fixed
132
+ doc = document
133
+ clear; Builder.new(doc, self, &block)
134
+
135
+ self
136
+ end
137
+
138
+ def inner_dom=(node)
139
+ clear; self << node
140
+ end
141
+
142
+ def /(*paths)
143
+ paths.map { |path| xpath(path) }.flatten.uniq
144
+ end
145
+
146
+ def at(path)
147
+ xpath(path).first || css(path).first
148
+ end
149
+
150
+ def at_css(*rules)
151
+ rules.each {|rule|
152
+ found = css(rule).first
153
+
154
+ return found if found
155
+ }
156
+
157
+ nil
158
+ end
159
+
160
+ def at_xpath(*paths)
161
+ paths.each {|path|
162
+ found = xpath(path).first
163
+
164
+ return found if found
165
+ }
166
+
167
+ nil
168
+ end
169
+
170
+ def search(*selectors)
171
+ NodeSet.new document, selectors.map {|selector|
172
+ xpath(selector).to_a.concat(css(selector).to_a)
173
+ }.flatten.uniq
174
+ end
175
+
176
+ def css(path)
177
+ NodeSet.new(document, Native::Array.new(`#@native.querySelectorAll(path)`))
178
+ end
179
+
180
+ def xpath(path)
181
+ result = []
182
+
183
+ begin
184
+ %x{
185
+ var tmp = (#@native.ownerDocument || #@native).evaluate(
186
+ path, #@native, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
187
+
188
+ result = #{Native::Array.new(`tmp`, get: :snapshotItem, length: :snapshotLength)};
189
+ }
190
+ rescue; end
191
+
192
+ NodeSet.new(document, result)
193
+ end
194
+
195
+ def style(data = nil, &block)
196
+ style = CSS::Declaration.new(`#@native.style`)
197
+
198
+ return style unless data || block
199
+
200
+ if data.is_a?(String)
201
+ style.replace(data)
202
+ elsif data.is_a?(Enumerable)
203
+ style.assign(data)
204
+ end
205
+
206
+ if block
207
+ style.apply(&block)
208
+ end
209
+
210
+ self
211
+ end
212
+
213
+ def style!
214
+ CSS::Declaration.new(`#{window.to_n}.getComputedStyle(#@native, null)`)
215
+ end
216
+
217
+ def data(what)
218
+ unless defined?(`#@native.$data`)
219
+ `#@native.$data = {}`
220
+ end
221
+
222
+ if Hash === what
223
+ what.each {|name, value|
224
+ `#@native.$data[name] = value`
225
+ }
226
+ else
227
+ %x{
228
+ var value = #@native.$data[what];
229
+
230
+ if (value === undefined) {
231
+ return nil;
232
+ }
233
+ else {
234
+ return value;
235
+ }
236
+ }
237
+ end
238
+ end
239
+
240
+ def matches?(selector)
241
+ `#@native.matches(#{selector})`
242
+ end
243
+
244
+ def window
245
+ document.window
246
+ end
247
+
248
+ def inspect
249
+ "#<DOM::Element: #{name}>"
250
+ end
251
+
252
+ class Attributes
253
+ include Enumerable
254
+
255
+ attr_reader :namespace
256
+
257
+ def initialize(element, options)
258
+ @element = element
259
+ @namespace = options[:namespace]
260
+ end
261
+
262
+ def each(&block)
263
+ return enum_for :each unless block_given?
264
+
265
+ @element.attribute_nodes.each {|attr|
266
+ yield attr.name, attr.value
267
+ }
268
+
269
+ self
270
+ end
271
+
272
+ def [](name)
273
+ @element.get_attribute(name, namespace: @namespace)
274
+ end
275
+
276
+ def []=(name, value)
277
+ @element.set_attribute(name, value, namespace: @namespace)
278
+ end
279
+
280
+ def merge!(hash)
281
+ hash.each {|name, value|
282
+ self[name] = value
283
+ }
284
+
285
+ self
286
+ end
287
+ end
288
+ end
289
+
290
+ end; end