opal-browser 0.2.0.beta1 → 0.2.0

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