opal-browser 0.2.0.beta1 → 0.2.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +22 -8
  3. data/Gemfile +1 -1
  4. data/README.md +59 -5
  5. data/index.html.erb +7 -4
  6. data/lib/opal-browser.rb +1 -0
  7. data/opal-browser.gemspec +1 -1
  8. data/opal/browser.rb +1 -0
  9. data/opal/browser/animation_frame.rb +26 -1
  10. data/opal/browser/canvas.rb +0 -10
  11. data/opal/browser/canvas/data.rb +0 -10
  12. data/opal/browser/canvas/gradient.rb +0 -10
  13. data/opal/browser/canvas/style.rb +0 -10
  14. data/opal/browser/canvas/text.rb +0 -10
  15. data/opal/browser/cookies.rb +6 -8
  16. data/opal/browser/database/sql.rb +194 -0
  17. data/opal/browser/delay.rb +25 -7
  18. data/opal/browser/dom.rb +2 -11
  19. data/opal/browser/dom/attribute.rb +12 -11
  20. data/opal/browser/dom/builder.rb +4 -9
  21. data/opal/browser/dom/document.rb +105 -41
  22. data/opal/browser/dom/element.rb +317 -231
  23. data/opal/browser/dom/element/attributes.rb +87 -0
  24. data/opal/browser/dom/element/data.rb +67 -0
  25. data/opal/browser/dom/element/input.rb +12 -1
  26. data/opal/browser/dom/element/offset.rb +5 -0
  27. data/opal/browser/dom/element/position.rb +11 -2
  28. data/opal/browser/dom/element/scroll.rb +77 -10
  29. data/opal/browser/dom/element/select.rb +36 -0
  30. data/opal/browser/dom/element/size.rb +5 -0
  31. data/opal/browser/dom/element/template.rb +9 -0
  32. data/opal/browser/dom/element/textarea.rb +24 -0
  33. data/opal/browser/dom/mutation_observer.rb +2 -2
  34. data/opal/browser/dom/node.rb +93 -51
  35. data/opal/browser/dom/node_set.rb +66 -48
  36. data/opal/browser/effects.rb +11 -0
  37. data/opal/browser/{dom/event.rb → event.rb} +32 -32
  38. data/opal/browser/{dom/event → event}/animation.rb +2 -2
  39. data/opal/browser/{dom/event → event}/audio_processing.rb +2 -2
  40. data/opal/browser/{dom/event → event}/base.rb +65 -7
  41. data/opal/browser/{dom/event → event}/before_unload.rb +2 -2
  42. data/opal/browser/{dom/event → event}/clipboard.rb +2 -2
  43. data/opal/browser/{dom/event → event}/close.rb +2 -2
  44. data/opal/browser/{dom/event → event}/composition.rb +2 -2
  45. data/opal/browser/{dom/event → event}/custom.rb +2 -2
  46. data/opal/browser/{dom/event → event}/device_light.rb +2 -2
  47. data/opal/browser/{dom/event → event}/device_motion.rb +2 -2
  48. data/opal/browser/{dom/event → event}/device_orientation.rb +2 -2
  49. data/opal/browser/{dom/event → event}/device_proximity.rb +2 -2
  50. data/opal/browser/{dom/event → event}/drag.rb +2 -2
  51. data/opal/browser/{dom/event → event}/focus.rb +2 -2
  52. data/opal/browser/{dom/event → event}/gamepad.rb +2 -2
  53. data/opal/browser/{dom/event → event}/hash_change.rb +2 -2
  54. data/opal/browser/{dom/event → event}/keyboard.rb +2 -2
  55. data/opal/browser/{dom/event → event}/message.rb +2 -2
  56. data/opal/browser/{dom/event → event}/mouse.rb +2 -2
  57. data/opal/browser/{dom/event → event}/page_transition.rb +2 -2
  58. data/opal/browser/{dom/event → event}/pop_state.rb +2 -2
  59. data/opal/browser/{dom/event → event}/progress.rb +2 -2
  60. data/opal/browser/{dom/event → event}/sensor.rb +2 -2
  61. data/opal/browser/{dom/event → event}/storage.rb +2 -2
  62. data/opal/browser/{dom/event → event}/touch.rb +2 -2
  63. data/opal/browser/{dom/event → event}/ui.rb +2 -2
  64. data/opal/browser/{dom/event → event}/wheel.rb +2 -2
  65. data/opal/browser/event_source.rb +1 -1
  66. data/opal/browser/http.rb +25 -0
  67. data/opal/browser/http/binary.rb +1 -0
  68. data/opal/browser/http/headers.rb +16 -2
  69. data/opal/browser/http/request.rb +14 -38
  70. data/opal/browser/immediate.rb +9 -3
  71. data/opal/browser/interval.rb +34 -11
  72. data/opal/browser/navigator.rb +23 -4
  73. data/opal/browser/screen.rb +1 -1
  74. data/opal/browser/socket.rb +5 -1
  75. data/opal/browser/storage.rb +51 -33
  76. data/opal/browser/support.rb +59 -4
  77. data/opal/browser/version.rb +1 -1
  78. data/opal/browser/window.rb +17 -9
  79. data/opal/browser/window/size.rb +17 -3
  80. data/opal/opal-browser.rb +1 -0
  81. data/spec/database/sql_spec.rb +131 -0
  82. data/spec/delay_spec.rb +38 -0
  83. data/spec/dom/attribute_spec.rb +49 -0
  84. data/spec/dom/builder_spec.rb +25 -8
  85. data/spec/dom/document_spec.rb +20 -0
  86. data/spec/dom/element/attributes_spec.rb +52 -0
  87. data/spec/dom/element_spec.rb +139 -4
  88. data/spec/dom/node_set_spec.rb +44 -0
  89. data/spec/interval_spec.rb +50 -0
  90. data/spec/runner.rb +46 -28
  91. data/spec/socket_spec.rb +1 -0
  92. data/spec/spec_helper.rb +0 -4
  93. data/spec/storage_spec.rb +1 -1
  94. metadata +57 -39
  95. data/opal/browser/http/parameters.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b0aa6c5110fd5e167607dbdea62c227163d95be
4
- data.tar.gz: 666fc17d0cb5be69e9acf8a2f2d526e4995b7496
3
+ metadata.gz: 7da4f18bcba46ae940082055c721673e46573149
4
+ data.tar.gz: f70ab45c0391a6e9924b0b29a3a01923a7f1bcd2
5
5
  SHA512:
6
- metadata.gz: bdb5103e7c85d23815225544ee28a504a024cc61beb58c5ca77e1af6c0b33853c6bc0b5cc530975039f28151572b423725e6abc7bf5aa22bd4fe82ec4ef68ed8
7
- data.tar.gz: 4c08aa42993c6e63623dae9d5b9b09ed722801bf771c1527c56bb0791807958ffbb2d5c80904ae457c0686cf15ea597176de7269f9862a0d014f7171d7597f35
6
+ metadata.gz: e193b212a1d022644320910c25550cfb9f1db89667f07ea1c594ef5c70aff102a3184b68099f7cd7e9e0d8f6d4904e1dc429c678fbe6fb913d20df64c2c5f610
7
+ data.tar.gz: 03ed1d8246750cae5bae479b3146de89217ab57af51e5e95d1889247e74a7e6a4fdfad38447f1d0e8290b356924dfa4fd46c899469fa3b95c2bc568ad9c8d644
@@ -16,10 +16,6 @@ env:
16
16
  - SELENIUM_BROWSER=firefox SELENIUM_VERSION=25
17
17
  - SELENIUM_BROWSER=firefox SELENIUM_VERSION=26
18
18
 
19
- - SELENIUM_BROWSER=safari SELENIUM_VERSION=5.1
20
- - SELENIUM_BROWSER=safari SELENIUM_VERSION=6.1
21
- - SELENIUM_BROWSER=safari SELENIUM_VERSION=7.0
22
-
23
19
  - SELENIUM_BROWSER=opera SELENIUM_VERSION=12.15
24
20
  - SELENIUM_BROWSER=opera SELENIUM_VERSION=12.16
25
21
 
@@ -30,19 +26,37 @@ env:
30
26
  - SELENIUM_BROWSER=ie SELENIUM_VERSION=10
31
27
  - SELENIUM_BROWSER=ie SELENIUM_VERSION=11
32
28
 
29
+ - SELENIUM_BROWSER=safari SELENIUM_VERSION=5.1
30
+ - SELENIUM_BROWSER=safari SELENIUM_VERSION=6.1
31
+ - SELENIUM_BROWSER=safari SELENIUM_VERSION=7.0
32
+
33
+ - SELENIUM_BROWSER=ipad SELENIUM_DEVICE="iPad 2 (5.0)"
34
+ - SELENIUM_BROWSER=iphone SELENIUM_DEVICE="iPhone 4S"
35
+ - SELENIUM_BROWSER=iphone SELENIUM_DEVICE="iPhone 5"
36
+ - SELENIUM_BROWSER=iphone SELENIUM_DEVICE="iPhone 5S"
37
+
38
+ - SELENIUM_BROWSER=android SELENIUM_DEVICE="Samsung Galaxy Nexus"
39
+ - SELENIUM_BROWSER=android SELENIUM_DEVICE="Motorola Razr Maxx HD"
40
+ - SELENIUM_BROWSER=android SELENIUM_DEVICE="LG Nexus 4"
41
+
33
42
  - SELENIUM_BROWSER=chrome SELENIUM_VERSION=30
34
43
  - SELENIUM_BROWSER=chrome SELENIUM_VERSION=31
35
44
 
36
45
  matrix:
37
46
  allow_failures:
38
- # I know these fail
39
- - env: SELENIUM_BROWSER=ie SELENIUM_VERSION=6
40
- - env: SELENIUM_BROWSER=ie SELENIUM_VERSION=7
41
-
42
47
  # ChromeDriver be broken
43
48
  - env: SELENIUM_BROWSER=chrome SELENIUM_VERSION=31
44
49
  - env: SELENIUM_BROWSER=chrome SELENIUM_VERSION=30
45
50
 
51
+ # Something's fishy
52
+ - env: SELENIUM_BROWSER=ipad SELENIUM_DEVICE="iPad 2 (5.0)"
53
+ - env: SELENIUM_BROWSER=iphone SELENIUM_DEVICE="iPhone 5"
54
+
55
+ # Android's driver be broken
56
+ - env: SELENIUM_BROWSER=android SELENIUM_DEVICE="Samsung Galaxy Nexus"
57
+ - env: SELENIUM_BROWSER=android SELENIUM_DEVICE="Motorola Razr Maxx HD"
58
+ - env: SELENIUM_BROWSER=android SELENIUM_DEVICE="LG Nexus 4"
59
+
46
60
  before_install:
47
61
  - gem update bundler
48
62
  - bundle --version
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
  gem 'rack'
6
6
  gem 'sinatra'
7
7
  gem 'sinatra-websocket'
8
- gem 'opal-rspec', '0.3.0.beta3'
8
+ gem 'opal-rspec', '0.4.0.beta1'
9
9
 
10
10
  # runner
11
11
  gem 'selenium-webdriver', require: false
data/README.md CHANGED
@@ -1,13 +1,34 @@
1
1
  Browser support for Opal
2
2
  ========================
3
3
 
4
- [![Build Status](https://secure.travis-ci.org/opal/opal-browser.png?branch=master)](http://travis-ci.org/opal/opal-browser)
5
- [![Gem Version](https://badge.fury.io/rb/opal-browser.png)](http://badge.fury.io/rb/opal-browser)
6
- [![Code Climate](https://codeclimate.com/github/opal/opal-browser.png)](https://codeclimate.com/github/opal/opal-browser)
4
+ [![Build Status](https://secure.travis-ci.org/opal/opal-browser.svg?branch=master)](http://travis-ci.org/opal/opal-browser)
5
+ [![Gem Version](https://badge.fury.io/rb/opal-browser.svg)](http://badge.fury.io/rb/opal-browser)
6
+ [![Code Climate](http://img.shields.io/codeclimate/github/opal/opal-browser.svg)](https://codeclimate.com/github/opal/opal-browser)
7
+
7
8
 
8
9
  This library aims to be a full-blown wrapper for all the browser API including
9
10
  HTML5.
10
11
 
12
+ Usage
13
+ =====
14
+
15
+ _Server side (config.ru, Rakefile, Rails, Sinatra, etc.)_
16
+
17
+ ```ruby
18
+ require 'opal-browser'
19
+ # Your server code here
20
+ ```
21
+
22
+ _Browser side_
23
+
24
+ ```ruby
25
+ require 'opal'
26
+ require 'browser'
27
+ # Your Opal code here
28
+ ```
29
+
30
+
31
+
11
32
  Features
12
33
  ========
13
34
  This is a list of the currently wrapped features and some details on them.
@@ -18,7 +39,7 @@ DOM support is complete as far as I know, it has a very Nokogiri feel to it
18
39
  with obvious differences where relevant (for instance, event handling).
19
40
 
20
41
  ```ruby
21
- $document.on 'dom:load' do
42
+ $document.ready do
22
43
  alert "yo dawg, I'm all loaded up in here"
23
44
  end
24
45
  ```
@@ -27,7 +48,7 @@ It also supports a markaby inspired builder DSL which generates DOM nodes
27
48
  directly instead of creating a string.
28
49
 
29
50
  ```ruby
30
- $document.on 'dom:load' do
51
+ $document.ready do
31
52
  DOM {
32
53
  div.info {
33
54
  span.red "I'm all cooked up."
@@ -89,6 +110,18 @@ EventSource
89
110
  -----------
90
111
  Event sources have been implemented and are easily configurable with blocks.
91
112
 
113
+ ```ruby
114
+ Browser::EventSource.new '/events' do |es|
115
+ es.on :message do |e|
116
+ alert e.data
117
+ end
118
+
119
+ es.on :custom do |e|
120
+ alert "custom #{e.data}"
121
+ end
122
+ end
123
+ ```
124
+
92
125
  History
93
126
  -------
94
127
  The HTML5 History API has been fully wrapped.
@@ -98,6 +131,27 @@ Storage
98
131
  The HTML5 Storage API has been wrapped and it exports a single Storage class
99
132
  that uses the most appropriate and available API to store data locally.
100
133
 
134
+ Database SQL
135
+ ------------
136
+ WebSQL has been fully wrapped.
137
+
138
+ ```ruby
139
+ db = Browser::Database::SQL.new 'test'
140
+ db.transaction {|t|
141
+ t.query('CREATE TABLE test(ID INTEGER PRIMARY KEY ASC, text TEXT)').then {
142
+ t.query('INSERT INTO test (id, text) VALUES(?, ?)', 1, 'huehue')
143
+ }.then {
144
+ t.query('INSERT INTO test (id, text) VALUES(?, ?)', 2, 'jajaja')
145
+ }.then {
146
+ t.query('SELECT * FROM test')
147
+ }.then {|r|
148
+ r.each {|row|
149
+ alert row.inspect
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
101
155
  Browser support
102
156
  ===============
103
157
 
@@ -7,16 +7,19 @@
7
7
  window.onerror = function(msg, url, line) {
8
8
  var error = document.createElement("div");
9
9
  error.setAttribute("id", "rspec-error");
10
- error.innerHTML = "Error: " + msg + "<br/>";
11
- error.innerHTML += "Line: " + line + "<br/>";
12
- error.innerHTML += "URL: " + url + "<br/>";
10
+ error.appendChild(document.createTextNode(msg));
13
11
 
14
12
  document.body.appendChild(error);
15
13
  }
16
14
  </script>
15
+
16
+ <script src="spec/json2.js"></script>
17
+ <script src="spec/sizzle.js"></script>
18
+ <script src="spec/wgxpath.install.js"></script>
19
+
20
+ <%= javascript_include_tag @server.main %>
17
21
  </head>
18
22
  <body>
19
- <%= javascript_include_tag @server.main %>
20
23
  </body>
21
24
  </html>
22
25
 
@@ -0,0 +1 @@
1
+ require 'opal/browser'
@@ -16,6 +16,6 @@ Gem::Specification.new {|s|
16
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
17
  s.require_paths = ['lib']
18
18
 
19
- s.add_dependency 'opal', '~> 0.6.0'
19
+ s.add_dependency 'opal'
20
20
  s.add_dependency 'paggio'
21
21
  }
@@ -5,6 +5,7 @@ require 'browser/version'
5
5
  require 'browser/utils'
6
6
  require 'browser/support'
7
7
 
8
+ require 'browser/event'
8
9
  require 'browser/window'
9
10
  require 'browser/dom'
10
11
  require 'browser/css'
@@ -1,6 +1,22 @@
1
1
  module Browser
2
2
 
3
+ # Allows you to wrap a block to use in an animation rendering cycle.
4
+ #
5
+ # @see https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame
3
6
  class AnimationFrame
7
+ def self.supported?
8
+ ['Animation.request',
9
+ 'Animation.request (Chrome)',
10
+ 'Animation.request (Firefox)',
11
+ 'Animation.request (Opera)',
12
+ 'Animation.request (Internet Explorer)'].any? {|feature|
13
+ Browser.supports? feature
14
+ }
15
+ end
16
+
17
+ # Execute the block to update an animation before the next repaint.
18
+ #
19
+ # @param window [Window] the window to request the frame on
4
20
  def initialize(window, &block)
5
21
  @window = window
6
22
  @native = window.to_n
@@ -28,6 +44,7 @@ class AnimationFrame
28
44
  `#@native.msRequestAnimationFrame(#{block.to_n})`
29
45
  end
30
46
  else
47
+ # Request the animation frame.
31
48
  def request
32
49
  raise NotImplementedError, 'window requestAnimationFrame unsupported'
33
50
  end
@@ -70,6 +87,7 @@ class AnimationFrame
70
87
  `#@native.msCancelRequestAnimationFrame(#@id)`
71
88
  end
72
89
  else
90
+ # Cancel the animation frame request.
73
91
  def cancel
74
92
  raise NotImplementedError, 'window cancelAnimationFrame unsupported'
75
93
  end
@@ -78,8 +96,15 @@ end
78
96
 
79
97
  end
80
98
 
99
+ module Kernel
100
+ # (see Browser::AnimationFrame.new)
101
+ def animation_frame(&block)
102
+ Browser::AnimationFrame.new($window, &block)
103
+ end
104
+ end
105
+
81
106
  class Proc
82
- # Execute a block to update an animation before the next repaint.
107
+ # (see Browser::AnimationFrame.new)
83
108
  def animation_frame
84
109
  Browser::AnimationFrame.new($window, &self)
85
110
  end
@@ -1,13 +1,3 @@
1
- #--
2
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
- # Version 2, December 2004
4
- #
5
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
- # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
- #
8
- # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
- #++
10
-
11
1
  require 'promise'
12
2
 
13
3
  require 'browser/canvas/style'
@@ -1,13 +1,3 @@
1
- #--
2
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
- # Version 2, December 2004
4
- #
5
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
- # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
- #
8
- # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
- #++
10
-
11
1
  module Browser; class Canvas
12
2
 
13
3
  class Data
@@ -1,13 +1,3 @@
1
- #--
2
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
- # Version 2, December 2004
4
- #
5
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
- # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
- #
8
- # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
- #++
10
-
11
1
  module Browser; class Canvas
12
2
 
13
3
  class Gradient
@@ -1,13 +1,3 @@
1
- #--
2
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
- # Version 2, December 2004
4
- #
5
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
- # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
- #
8
- # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
- #++
10
-
11
1
  module Browser; class Canvas
12
2
 
13
3
  class StyleObject
@@ -1,13 +1,3 @@
1
- #--
2
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
- # Version 2, December 2004
4
- #
5
- # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
- # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
- #
8
- # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
- #++
10
-
11
1
  module Browser; class Canvas
12
2
 
13
3
  class Text
@@ -43,17 +43,15 @@ class Cookies
43
43
 
44
44
  # Set a cookie.
45
45
  #
46
- # Options
47
- # -------
48
- # + **max_age** - the max age of the cookie in seconds
49
- # + **expires** - the expire date as {Time}
50
- # + **path** - the path where the cookie is valid on
51
- # + **domain** - the domain where the cookie is valid on
52
- # + **secure** - whether the cookie is secure or not
53
- #
54
46
  # @param name [String] the name of the cookie
55
47
  # @param value [Object] the data to set
56
48
  # @param options [Hash] the options for the cookie
49
+ #
50
+ # @option options [Integer] :max_age the max age of the cookie in seconds
51
+ # @option options [Time] :expires the expire date
52
+ # @option options [String] :path the path the cookie is valid on
53
+ # @option options [String] :domain the domain the cookie is valid on
54
+ # @option options [Boolean] :secure whether the cookie is secure or not
57
55
  def []=(name, value, options = {})
58
56
  `#@document.cookie = #{encode name, value.is_a?(String) ? value : JSON.dump(value), @options.merge(options)}`
59
57
  end
@@ -0,0 +1,194 @@
1
+ require 'promise'
2
+ require 'ostruct'
3
+
4
+ module Browser; module Database
5
+
6
+ class SQL
7
+ # Check if the browser supports WebSQL.
8
+ def self.supported?
9
+ Browser.supports? 'WebSQL'
10
+ end
11
+
12
+ class Error < StandardError
13
+ def self.new(error)
14
+ return super if self != Error
15
+
16
+ [Unknown, Database, Version, TooLarge, Quota, Syntax, Constraint, Timeout] \
17
+ [`error.code`].new(`error.message`)
18
+ end
19
+
20
+ Unknown = Class.new(self)
21
+ Database = Class.new(self)
22
+ Version = Class.new(self)
23
+ TooLarge = Class.new(self)
24
+ Quota = Class.new(self)
25
+ Syntax = Class.new(self)
26
+ Constraint = Class.new(self)
27
+ Timeout = Class.new(self)
28
+ end
29
+
30
+ include Native
31
+
32
+ # @return [String] the name of the database
33
+ attr_reader :name
34
+
35
+ # @return [String] the description for the database
36
+ attr_reader :description
37
+
38
+ # @return [Integer] the size constraint in bytes
39
+ attr_reader :size
40
+
41
+ # Open a database with the given name and options.
42
+ #
43
+ # @param name [String] the name for the database
44
+ # @param options [Hash] options to open the database
45
+ #
46
+ # @option options [String] :description the description for the database
47
+ # @option options [String] :version ('') the expected version of the database
48
+ # @option options [Integer] :size (5 * 1024 * 1024) the size constraint in bytes
49
+ def initialize(name, options = {})
50
+ @name = name
51
+ @description = options[:description] || name
52
+ @version = options[:version] || ''
53
+ @size = options[:size] || 2 * 1024 * 1024
54
+
55
+ super(`window.openDatabase(#{name}, #{@version}, #{@description}, #{@size})`)
56
+ end
57
+
58
+ # @overload version()
59
+ #
60
+ # Get the version of the database.
61
+ #
62
+ # @return [String]
63
+ #
64
+ # @overload version(from, to, &block)
65
+ #
66
+ # Migrate the database to a new version.
67
+ #
68
+ # @param from [String] the version you're migrating from
69
+ # @param to [String] the version you're migrating to
70
+ #
71
+ # @yieldparam transaction [Transaction] the transaction to work with
72
+ def version(from = nil, to = nil, &block)
73
+ return `#@native.version` unless block
74
+
75
+ `#@native.changeVersion(#{from}, #{to},
76
+ #{-> t { block.call(Transaction.new(self, t)) }})`
77
+ end
78
+
79
+ # Start a transaction on the database.
80
+ #
81
+ # @yieldparam transaction [Transaction] the transaction to work on
82
+ def transaction(&block)
83
+ raise ArgumentError, 'no block given' unless block
84
+
85
+ `#@native.transaction(#{-> t { block.call(Transaction.new(self, t)) }})`
86
+ end
87
+
88
+ # Allows you to make changes to the database or read data from it.
89
+ class Transaction
90
+ include Native
91
+
92
+ # @return [Database] the database the transaction has been created from
93
+ attr_reader :database
94
+
95
+ # @private
96
+ def initialize(database, transaction)
97
+ @database = database
98
+
99
+ super(transaction)
100
+ end
101
+
102
+ # Query the database.
103
+ #
104
+ # @param query [String] the SQL query to send
105
+ # @param parameters [Array] optional bind parameters for the query
106
+ #
107
+ # @return [Promise]
108
+ def query(query, *parameters)
109
+ promise = Promise.new
110
+
111
+ `#@native.executeSql(#{query}, #{parameters},
112
+ #{-> _, r { promise.resolve(Result.new(self, r)) }},
113
+ #{-> _, e { promise.reject(Error.new(e)) }})`
114
+
115
+ promise
116
+ end
117
+ end
118
+
119
+ # Represents a row.
120
+ class Row < OpenStruct
121
+ # @private
122
+ def initialize(row)
123
+ super(Hash.new(row))
124
+ end
125
+
126
+ def inspect
127
+ "#<SQL::Row: #{Hash.new(@native).inspect}>"
128
+ end
129
+ end
130
+
131
+ class Result
132
+ include Native
133
+
134
+ # @return [Transaction] the transaction the result came from
135
+ attr_reader :transaction
136
+
137
+ # @return [SQL] the database the result came from
138
+ attr_reader :database
139
+
140
+ # @private
141
+ def initialize(transaction, result)
142
+ @transaction = transaction
143
+ @database = transaction.database
144
+
145
+ super(result)
146
+ end
147
+
148
+ include Enumerable
149
+
150
+ # Get a row from the result.
151
+ #
152
+ # @param index [Integer] the index for the row
153
+ #
154
+ # @return [Row]
155
+ def [](index)
156
+ if index < 0
157
+ index += length
158
+ end
159
+
160
+ unless index < 0 || index >= length
161
+ Row.new(`#@native.rows.item(index)`)
162
+ end
163
+ end
164
+
165
+ # Enumerate over the rows.
166
+ #
167
+ # @yieldparam row [Row]
168
+ #
169
+ # @return [self]
170
+ def each(&block)
171
+ return enum_for :each unless block
172
+
173
+ %x{
174
+ for (var i = 0, length = #@native.rows.length; i < length; i++) {
175
+ #{block.call(self[`i`])};
176
+ }
177
+ }
178
+
179
+ self
180
+ end
181
+
182
+ # @!attribute [r] length
183
+ # @return [Integer] number of rows in the result
184
+ def length
185
+ `#@native.rows.length`
186
+ end
187
+
188
+ # @!attribute [r] affected
189
+ # @return [Integer] number of affected rows
190
+ alias_native :affected, :rowsAffected
191
+ end
192
+ end
193
+
194
+ end; end