rhodes-framework 1.0.10 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (198) hide show
  1. data/Manifest.txt +159 -30
  2. data/Rakefile +3 -19
  3. data/lib/erb.rb +1 -1
  4. data/lib/rho/rho.rb +94 -107
  5. data/lib/rho/rhofsconnector.rb +4 -0
  6. data/lib/rho/rhoutils.rb +26 -0
  7. data/lib/rho/rhoviewhelpers.rb +1 -1
  8. data/lib/rhodes.rb +2 -2
  9. data/lib/rhoframework.rb +9 -9
  10. data/lib/rhom/rhom.rb +1 -1
  11. data/lib/rhom/rhom_db_adapter.rb +36 -15
  12. data/lib/rhom/rhom_db_adapterME.rb +2 -3
  13. data/lib/rhom/rhom_object.rb +14 -2
  14. data/lib/rhom/rhom_object_factory.rb +42 -11
  15. data/lib/version.rb +2 -2
  16. data/spec/README +1 -0
  17. data/spec/Rakefile +1 -0
  18. data/spec/{configs/account.rb → app/Account/config.rb} +0 -0
  19. data/spec/{configs/case.rb → app/Case/config.rb} +0 -0
  20. data/spec/app/Question/config.rb +3 -0
  21. data/spec/app/Settings/controller.rb +10 -0
  22. data/spec/app/Settings/index.erb +11 -0
  23. data/spec/app/SpecRunner/controller.rb +15 -0
  24. data/spec/app/SpecRunner/index.erb +14 -0
  25. data/spec/app/application.rb +4 -0
  26. data/spec/app/index.erb +17 -0
  27. data/spec/app/layout.erb +27 -0
  28. data/spec/app/loading.html +11 -0
  29. data/spec/app/mspec.rb +11 -0
  30. data/spec/app/mspec/expectations.rb +2 -0
  31. data/spec/app/mspec/expectations/expectations.rb +17 -0
  32. data/spec/app/mspec/expectations/should.rb +25 -0
  33. data/spec/app/mspec/fileutils.rb +1590 -0
  34. data/spec/app/mspec/guards.rb +16 -0
  35. data/spec/app/mspec/guards/background.rb +21 -0
  36. data/spec/app/mspec/guards/bug.rb +24 -0
  37. data/spec/app/mspec/guards/compliance.rb +37 -0
  38. data/spec/app/mspec/guards/conflict.rb +18 -0
  39. data/spec/app/mspec/guards/endian.rb +44 -0
  40. data/spec/app/mspec/guards/extensions.rb +20 -0
  41. data/spec/app/mspec/guards/guard.rb +166 -0
  42. data/spec/app/mspec/guards/noncompliance.rb +20 -0
  43. data/spec/app/mspec/guards/platform.rb +43 -0
  44. data/spec/app/mspec/guards/quarantine.rb +17 -0
  45. data/spec/app/mspec/guards/runner.rb +34 -0
  46. data/spec/app/mspec/guards/superuser.rb +17 -0
  47. data/spec/app/mspec/guards/support.rb +20 -0
  48. data/spec/app/mspec/guards/tty.rb +20 -0
  49. data/spec/app/mspec/guards/version.rb +38 -0
  50. data/spec/app/mspec/helpers.rb +11 -0
  51. data/spec/app/mspec/helpers/argv.rb +43 -0
  52. data/spec/app/mspec/helpers/bignum.rb +5 -0
  53. data/spec/app/mspec/helpers/const_lookup.rb +9 -0
  54. data/spec/app/mspec/helpers/environment.rb +23 -0
  55. data/spec/app/mspec/helpers/fixture.rb +20 -0
  56. data/spec/app/mspec/helpers/flunk.rb +5 -0
  57. data/spec/app/mspec/helpers/io.rb +17 -0
  58. data/spec/app/mspec/helpers/language_version.rb +20 -0
  59. data/spec/app/mspec/helpers/ruby_exe.rb +123 -0
  60. data/spec/app/mspec/helpers/scratch.rb +17 -0
  61. data/spec/app/mspec/helpers/tmp.rb +32 -0
  62. data/spec/app/mspec/matchers.rb +23 -0
  63. data/spec/app/mspec/matchers/base.rb +95 -0
  64. data/spec/app/mspec/matchers/be_an_instance_of.rb +26 -0
  65. data/spec/app/mspec/matchers/be_ancestor_of.rb +24 -0
  66. data/spec/app/mspec/matchers/be_close.rb +27 -0
  67. data/spec/app/mspec/matchers/be_empty.rb +20 -0
  68. data/spec/app/mspec/matchers/be_false.rb +20 -0
  69. data/spec/app/mspec/matchers/be_kind_of.rb +24 -0
  70. data/spec/app/mspec/matchers/be_nil.rb +20 -0
  71. data/spec/app/mspec/matchers/be_true.rb +20 -0
  72. data/spec/app/mspec/matchers/complain.rb +56 -0
  73. data/spec/app/mspec/matchers/eql.rb +26 -0
  74. data/spec/app/mspec/matchers/equal.rb +26 -0
  75. data/spec/app/mspec/matchers/equal_element.rb +78 -0
  76. data/spec/app/mspec/matchers/equal_utf16.rb +34 -0
  77. data/spec/app/mspec/matchers/have_constant.rb +30 -0
  78. data/spec/app/mspec/matchers/have_instance_method.rb +24 -0
  79. data/spec/app/mspec/matchers/have_method.rb +24 -0
  80. data/spec/app/mspec/matchers/have_private_instance_method.rb +24 -0
  81. data/spec/app/mspec/matchers/include.rb +32 -0
  82. data/spec/app/mspec/matchers/match_yaml.rb +47 -0
  83. data/spec/app/mspec/matchers/method.rb +14 -0
  84. data/spec/app/mspec/matchers/output.rb +67 -0
  85. data/spec/app/mspec/matchers/output_to_fd.rb +71 -0
  86. data/spec/app/mspec/matchers/raise_error.rb +48 -0
  87. data/spec/app/mspec/matchers/respond_to.rb +24 -0
  88. data/spec/app/mspec/matchers/stringsymboladapter.rb +8 -0
  89. data/spec/app/mspec/mocks.rb +3 -0
  90. data/spec/app/mspec/mocks/mock.rb +159 -0
  91. data/spec/app/mspec/mocks/object.rb +20 -0
  92. data/spec/app/mspec/mocks/proxy.rb +136 -0
  93. data/spec/app/mspec/pp.rb +893 -0
  94. data/spec/app/mspec/runner.rb +15 -0
  95. data/spec/app/mspec/runner/actions.rb +8 -0
  96. data/spec/app/mspec/runner/actions/debug.rb +17 -0
  97. data/spec/app/mspec/runner/actions/filter.rb +40 -0
  98. data/spec/app/mspec/runner/actions/gdb.rb +17 -0
  99. data/spec/app/mspec/runner/actions/tag.rb +133 -0
  100. data/spec/app/mspec/runner/actions/taglist.rb +56 -0
  101. data/spec/app/mspec/runner/actions/tagpurge.rb +56 -0
  102. data/spec/app/mspec/runner/actions/tally.rb +116 -0
  103. data/spec/app/mspec/runner/actions/timer.rb +22 -0
  104. data/spec/app/mspec/runner/context.rb +188 -0
  105. data/spec/app/mspec/runner/example.rb +34 -0
  106. data/spec/app/mspec/runner/exception.rb +43 -0
  107. data/spec/app/mspec/runner/filters.rb +4 -0
  108. data/spec/app/mspec/runner/filters/match.rb +22 -0
  109. data/spec/app/mspec/runner/filters/profile.rb +54 -0
  110. data/spec/app/mspec/runner/filters/regexp.rb +7 -0
  111. data/spec/app/mspec/runner/filters/tag.rb +29 -0
  112. data/spec/app/mspec/runner/formatters.rb +10 -0
  113. data/spec/app/mspec/runner/formatters/describe.rb +24 -0
  114. data/spec/app/mspec/runner/formatters/dotted.rb +98 -0
  115. data/spec/app/mspec/runner/formatters/file.rb +19 -0
  116. data/spec/app/mspec/runner/formatters/html.rb +81 -0
  117. data/spec/app/mspec/runner/formatters/method.rb +93 -0
  118. data/spec/app/mspec/runner/formatters/specdoc.rb +41 -0
  119. data/spec/app/mspec/runner/formatters/spinner.rb +99 -0
  120. data/spec/app/mspec/runner/formatters/summary.rb +11 -0
  121. data/spec/app/mspec/runner/formatters/unit.rb +21 -0
  122. data/spec/app/mspec/runner/formatters/yaml.rb +44 -0
  123. data/spec/app/mspec/runner/mspec.rb +361 -0
  124. data/spec/app/mspec/runner/object.rb +24 -0
  125. data/spec/app/mspec/runner/shared.rb +12 -0
  126. data/spec/app/mspec/runner/tag.rb +32 -0
  127. data/spec/app/mspec/utils/name_map.rb +129 -0
  128. data/spec/app/mspec/utils/options.rb +441 -0
  129. data/spec/app/mspec/utils/ruby_name.rb +8 -0
  130. data/spec/app/mspec/utils/script.rb +220 -0
  131. data/spec/app/mspec/utils/version.rb +53 -0
  132. data/spec/app/mspec/version.rb +5 -0
  133. data/spec/app/spec/fixtures/client_info.txt +2 -0
  134. data/spec/app/spec/fixtures/object_values.txt +90 -0
  135. data/spec/{rho_controller_spec.rb → app/spec/rho_controller_spec.rb} +4 -7
  136. data/spec/{rho_spec.rb → app/spec/rho_spec.rb} +15 -36
  137. data/spec/{rhom_object_factory_spec.rb → app/spec/rhom_object_factory_spec.rb} +108 -72
  138. data/spec/{rhom_spec.rb → app/spec/rhom_spec.rb} +8 -4
  139. data/spec/app/spec/spec_helper.rb +15 -0
  140. data/spec/app/spec/webview_spec.rb +27 -0
  141. data/spec/app/spec_runner.rb +26 -0
  142. data/spec/build.yml +28 -0
  143. data/spec/public/css/base.css +39 -0
  144. data/spec/public/css/blackberry.css +99 -0
  145. data/spec/public/css/iphone.css +392 -0
  146. data/spec/public/css/rho.css +3 -0
  147. data/spec/public/css/xhtml.css +114 -0
  148. data/spec/public/images/IUI_LICENSE.txt +21 -0
  149. data/spec/public/images/backButton.png +0 -0
  150. data/spec/public/images/blueButton.png +0 -0
  151. data/spec/public/images/cancel.png +0 -0
  152. data/spec/public/images/grayButton.png +0 -0
  153. data/spec/public/images/iui-logo-touch-icon.png +0 -0
  154. data/spec/public/images/listArrow.png +0 -0
  155. data/spec/public/images/listArrowSel.png +0 -0
  156. data/spec/public/images/listGroup.png +0 -0
  157. data/spec/public/images/loading.gif +0 -0
  158. data/spec/public/images/pinstripes.png +0 -0
  159. data/spec/public/images/right_button.png +0 -0
  160. data/spec/public/images/selection.png +0 -0
  161. data/spec/public/images/thumb.png +0 -0
  162. data/spec/public/images/toggle.png +0 -0
  163. data/spec/public/images/toggleOn.png +0 -0
  164. data/spec/public/images/toolButton.png +0 -0
  165. data/spec/public/images/toolButton_new.png +0 -0
  166. data/spec/public/images/toolbar.png +0 -0
  167. data/spec/public/images/whiteButton.png +0 -0
  168. data/spec/public/js/application.js +1 -0
  169. data/spec/public/js/jquery-1.2.6.min.js +32 -0
  170. data/spec/public/js/rho.js +4 -0
  171. data/spec/public/js/rhogeolocation-wm.js +59 -0
  172. data/spec/public/js/rhogeolocation.js +11 -0
  173. data/spec/rhoconfig.txt +19 -0
  174. metadata +169 -39
  175. data/History.txt +0 -37
  176. data/README.rdoc +0 -2
  177. data/lib/TestServe.rb +0 -9
  178. data/res/sqlite3/constants.rb +0 -49
  179. data/res/sqlite3/database.rb +0 -715
  180. data/res/sqlite3/driver/dl/api.rb +0 -154
  181. data/res/sqlite3/driver/dl/driver.rb +0 -307
  182. data/res/sqlite3/driver/native/driver.rb +0 -257
  183. data/res/sqlite3/errors.rb +0 -68
  184. data/res/sqlite3/pragmas.rb +0 -271
  185. data/res/sqlite3/resultset.rb +0 -176
  186. data/res/sqlite3/sqlite3_api.rb +0 -0
  187. data/res/sqlite3/statement.rb +0 -230
  188. data/res/sqlite3/translator.rb +0 -109
  189. data/res/sqlite3/value.rb +0 -57
  190. data/res/sqlite3/version.rb +0 -14
  191. data/rhodes.gemspec +0 -18
  192. data/spec/app_manifest.txt +0 -4
  193. data/spec/configs/contact.rb +0 -3
  194. data/spec/configs/employee.rb +0 -3
  195. data/spec/spec.opts +0 -1
  196. data/spec/spec_helper.rb +0 -49
  197. data/spec/stubs.rb +0 -39
  198. data/spec/syncdbtest.sqlite +0 -0
data/History.txt DELETED
@@ -1,37 +0,0 @@
1
- == 0.2.6 2009-01-29
2
- * changed signature of url_for, link_to, and redirect
3
-
4
- == 0.2.5 2009-01-28
5
- * erb labels are now humanized
6
-
7
- == 0.2.4 2009-01-27
8
- * added show.erb to model generator [#164]
9
- * base source_adapter should include user_id on sync
10
-
11
- == 0.2.3 2009-01-26
12
- * added newgem dependency
13
-
14
- == 0.2.2 2009-01-23
15
- * fixed bug in layout.erb template
16
-
17
- == 0.2.1 2009-01-20
18
- * adding new layout framework to generator [#45]
19
-
20
- == 0.2.0 2009-01-07
21
- * releasing 0.2 version of gem
22
- * fixed sqlite3-ruby 1.2.4 dependency [#147]
23
- * fixed broken rspec tests [#148]
24
-
25
- == 0.1.3 2008-12-11
26
- * Updated source_adapter generator to have base class
27
- * fixed #50, removed dependency on Find library
28
-
29
- == 0.1.2 2008-12-09
30
- * Added rhogen source <MySourceAdapter>
31
-
32
- == 0.1.1 2008-11-18
33
- * Fixed template application name
34
-
35
- == 0.1.0 2008-11-18
36
- * 1 major enhancement:
37
- * Initial release
data/README.rdoc DELETED
@@ -1,2 +0,0 @@
1
- = rhodes framework
2
- * See rhodes/README.rdoc for more information
data/lib/TestServe.rb DELETED
@@ -1,9 +0,0 @@
1
- require 'rho'
2
- puts 'RHO loaded'
3
- r = Rho::RHO.new
4
- req = Hash.new
5
- req['application']="Rhosugar"
6
- req['model'] = 'Account'
7
- req['request-method']='GET'
8
- puts r.serve(req)
9
-
@@ -1,49 +0,0 @@
1
- module SQLite3 ; module Constants
2
-
3
- module TextRep
4
- UTF8 = 1
5
- UTF16LE = 2
6
- UTF16BE = 3
7
- UTF16 = 4
8
- ANY = 5
9
- end
10
-
11
- module ColumnType
12
- INTEGER = 1
13
- FLOAT = 2
14
- TEXT = 3
15
- BLOB = 4
16
- NULL = 5
17
- end
18
-
19
- module ErrorCode
20
- OK = 0 # Successful result
21
- ERROR = 1 # SQL error or missing database
22
- INTERNAL = 2 # An internal logic error in SQLite
23
- PERM = 3 # Access permission denied
24
- ABORT = 4 # Callback routine requested an abort
25
- BUSY = 5 # The database file is locked
26
- LOCKED = 6 # A table in the database is locked
27
- NOMEM = 7 # A malloc() failed
28
- READONLY = 8 # Attempt to write a readonly database
29
- INTERRUPT = 9 # Operation terminated by sqlite_interrupt()
30
- IOERR = 10 # Some kind of disk I/O error occurred
31
- CORRUPT = 11 # The database disk image is malformed
32
- NOTFOUND = 12 # (Internal Only) Table or record not found
33
- FULL = 13 # Insertion failed because database is full
34
- CANTOPEN = 14 # Unable to open the database file
35
- PROTOCOL = 15 # Database lock protocol error
36
- EMPTY = 16 # (Internal Only) Database table is empty
37
- SCHEMA = 17 # The database schema changed
38
- TOOBIG = 18 # Too much data for one row of a table
39
- CONSTRAINT = 19 # Abort due to contraint violation
40
- MISMATCH = 20 # Data type mismatch
41
- MISUSE = 21 # Library used incorrectly
42
- NOLFS = 22 # Uses OS features not supported on host
43
- AUTH = 23 # Authorization denied
44
-
45
- ROW = 100 # sqlite_step() has another row ready
46
- DONE = 101 # sqlite_step() has finished executing
47
- end
48
-
49
- end ; end
@@ -1,715 +0,0 @@
1
- require 'sqlite3/constants'
2
- require 'sqlite3/errors'
3
- require 'sqlite3/pragmas'
4
- require 'sqlite3/statement'
5
- require 'sqlite3/translator'
6
- require 'sqlite3/value'
7
-
8
- module SQLite3
9
-
10
- # The Database class encapsulates a single connection to a SQLite3 database.
11
- # Its usage is very straightforward:
12
- #
13
- # require 'sqlite3'
14
- #
15
- # db = SQLite3::Database.new( "data.db" )
16
- #
17
- # db.execute( "select * from table" ) do |row|
18
- # p row
19
- # end
20
- #
21
- # db.close
22
- #
23
- # It wraps the lower-level methods provides by the selected driver, and
24
- # includes the Pragmas module for access to various pragma convenience
25
- # methods.
26
- #
27
- # The Database class provides type translation services as well, by which
28
- # the SQLite3 data types (which are all represented as strings) may be
29
- # converted into their corresponding types (as defined in the schemas
30
- # for their tables). This translation only occurs when querying data from
31
- # the database--insertions and updates are all still typeless.
32
- #
33
- # Furthermore, the Database class has been designed to work well with the
34
- # ArrayFields module from Ara Howard. If you require the ArrayFields
35
- # module before performing a query, and if you have not enabled results as
36
- # hashes, then the results will all be indexible by field name.
37
- class Database
38
- include Pragmas
39
-
40
- class <<self
41
-
42
- alias :open :new
43
-
44
- # Quotes the given string, making it safe to use in an SQL statement.
45
- # It replaces all instances of the single-quote character with two
46
- # single-quote characters. The modified string is returned.
47
- def quote( string )
48
- string.gsub( /'/, "''" )
49
- end
50
-
51
- end
52
-
53
- # The low-level opaque database handle that this object wraps.
54
- attr_reader :handle
55
-
56
- # A reference to the underlying SQLite3 driver used by this database.
57
- attr_reader :driver
58
-
59
- # A boolean that indicates whether rows in result sets should be returned
60
- # as hashes or not. By default, rows are returned as arrays.
61
- attr_accessor :results_as_hash
62
-
63
- # A boolean indicating whether or not type translation is enabled for this
64
- # database.
65
- attr_accessor :type_translation
66
-
67
- # Create a new Database object that opens the given file. If utf16
68
- # is +true+, the filename is interpreted as a UTF-16 encoded string.
69
- #
70
- # By default, the new database will return result rows as arrays
71
- # (#results_as_hash) and has type translation disabled (#type_translation=).
72
- def initialize( file_name, options={} )
73
- utf16 = options.fetch(:utf16, false)
74
- load_driver( options[:driver] )
75
-
76
- @statement_factory = options[:statement_factory] || Statement
77
-
78
- result, @handle = @driver.open( file_name, utf16 )
79
- Error.check( result, self, "could not open database" )
80
-
81
- @closed = false
82
- @results_as_hash = options.fetch(:results_as_hash,false)
83
- @type_translation = options.fetch(:type_translation,false)
84
- @translator = nil
85
- @transaction_active = false
86
- end
87
-
88
- # Return +true+ if the string is a valid (ie, parsable) SQL statement, and
89
- # +false+ otherwise. If +utf16+ is +true+, then the string is a UTF-16
90
- # character string.
91
- def complete?( string, utf16=false )
92
- @driver.complete?( string, utf16 )
93
- end
94
-
95
- # Return a string describing the last error to have occurred with this
96
- # database.
97
- def errmsg( utf16=false )
98
- @driver.errmsg( @handle, utf16 )
99
- end
100
-
101
- # Return an integer representing the last error to have occurred with this
102
- # database.
103
- def errcode
104
- @driver.errcode( @handle )
105
- end
106
-
107
- # Return the type translator employed by this database instance. Each
108
- # database instance has its own type translator; this allows for different
109
- # type handlers to be installed in each instance without affecting other
110
- # instances. Furthermore, the translators are instantiated lazily, so that
111
- # if a database does not use type translation, it will not be burdened by
112
- # the overhead of a useless type translator. (See the Translator class.)
113
- def translator
114
- @translator ||= Translator.new
115
- end
116
-
117
- # Closes this database.
118
- def close
119
- unless @closed
120
- result = @driver.close( @handle )
121
- Error.check( result, self )
122
- end
123
- @closed = true
124
- end
125
-
126
- # Returns +true+ if this database instance has been closed (see #close).
127
- def closed?
128
- @closed
129
- end
130
-
131
- # Installs (or removes) a block that will be invoked for every SQL
132
- # statement executed. The block receives a two parameters: the +data+
133
- # argument, and the SQL statement executed. If the block is +nil+,
134
- # any existing tracer will be uninstalled.
135
- def trace( data=nil, &block )
136
- @driver.trace( @handle, data, &block )
137
- end
138
-
139
- # Installs (or removes) a block that will be invoked for every access
140
- # to the database. If the block returns 0 (or +nil+), the statement
141
- # is allowed to proceed. Returning 1 causes an authorization error to
142
- # occur, and returning 2 causes the access to be silently denied.
143
- def authorizer( data=nil, &block )
144
- result = @driver.set_authorizer( @handle, data, &block )
145
- Error.check( result, self )
146
- end
147
-
148
- # Returns a Statement object representing the given SQL. This does not
149
- # execute the statement; it merely prepares the statement for execution.
150
- #
151
- # The Statement can then be executed using Statement#execute.
152
- #
153
- def prepare( sql )
154
- stmt = @statement_factory.new( self, sql )
155
- if block_given?
156
- begin
157
- yield stmt
158
- ensure
159
- stmt.close
160
- end
161
- else
162
- return stmt
163
- end
164
- end
165
-
166
- # Executes the given SQL statement. If additional parameters are given,
167
- # they are treated as bind variables, and are bound to the placeholders in
168
- # the query.
169
- #
170
- # Note that if any of the values passed to this are hashes, then the
171
- # key/value pairs are each bound separately, with the key being used as
172
- # the name of the placeholder to bind the value to.
173
- #
174
- # The block is optional. If given, it will be invoked for each row returned
175
- # by the query. Otherwise, any results are accumulated into an array and
176
- # returned wholesale.
177
- #
178
- # See also #execute2, #query, and #execute_batch for additional ways of
179
- # executing statements.
180
- def execute( sql, *bind_vars )
181
- prepare( sql ) do |stmt|
182
- result = stmt.execute( *bind_vars )
183
- if block_given?
184
- result.each { |row| yield row }
185
- else
186
- return result.inject( [] ) { |arr,row| arr << row; arr }
187
- end
188
- end
189
- end
190
-
191
- # Executes the given SQL statement, exactly as with #execute. However, the
192
- # first row returned (either via the block, or in the returned array) is
193
- # always the names of the columns. Subsequent rows correspond to the data
194
- # from the result set.
195
- #
196
- # Thus, even if the query itself returns no rows, this method will always
197
- # return at least one row--the names of the columns.
198
- #
199
- # See also #execute, #query, and #execute_batch for additional ways of
200
- # executing statements.
201
- def execute2( sql, *bind_vars )
202
- prepare( sql ) do |stmt|
203
- result = stmt.execute( *bind_vars )
204
- if block_given?
205
- yield result.columns
206
- result.each { |row| yield row }
207
- else
208
- return result.inject( [ result.columns ] ) { |arr,row|
209
- arr << row; arr }
210
- end
211
- end
212
- end
213
-
214
- # Executes all SQL statements in the given string. By contrast, the other
215
- # means of executing queries will only execute the first statement in the
216
- # string, ignoring all subsequent statements. This will execute each one
217
- # in turn. The same bind parameters, if given, will be applied to each
218
- # statement.
219
- #
220
- # This always returns +nil+, making it unsuitable for queries that return
221
- # rows.
222
- def execute_batch( sql, *bind_vars )
223
- sql = sql.strip
224
- until sql.empty? do
225
- prepare( sql ) do |stmt|
226
- stmt.execute( *bind_vars )
227
- sql = stmt.remainder.strip
228
- end
229
- end
230
- nil
231
- end
232
-
233
- # This is a convenience method for creating a statement, binding
234
- # paramters to it, and calling execute:
235
- #
236
- # result = db.query( "select * from foo where a=?", 5 )
237
- # # is the same as
238
- # result = db.prepare( "select * from foo where a=?" ).execute( 5 )
239
- #
240
- # You must be sure to call +close+ on the ResultSet instance that is
241
- # returned, or you could have problems with locks on the table. If called
242
- # with a block, +close+ will be invoked implicitly when the block
243
- # terminates.
244
- def query( sql, *bind_vars )
245
- result = prepare( sql ).execute( *bind_vars )
246
- if block_given?
247
- begin
248
- yield result
249
- ensure
250
- result.close
251
- end
252
- else
253
- return result
254
- end
255
- end
256
-
257
- # A convenience method for obtaining the first row of a result set, and
258
- # discarding all others. It is otherwise identical to #execute.
259
- #
260
- # See also #get_first_value.
261
- def get_first_row( sql, *bind_vars )
262
- execute( sql, *bind_vars ) { |row| return row }
263
- nil
264
- end
265
-
266
- # A convenience method for obtaining the first value of the first row of a
267
- # result set, and discarding all other values and rows. It is otherwise
268
- # identical to #execute.
269
- #
270
- # See also #get_first_row.
271
- def get_first_value( sql, *bind_vars )
272
- execute( sql, *bind_vars ) { |row| return row[0] }
273
- nil
274
- end
275
-
276
- # Obtains the unique row ID of the last row to be inserted by this Database
277
- # instance.
278
- def last_insert_row_id
279
- @driver.last_insert_rowid( @handle )
280
- end
281
-
282
- # Returns the number of changes made to this database instance by the last
283
- # operation performed. Note that a "delete from table" without a where
284
- # clause will not affect this value.
285
- def changes
286
- @driver.changes( @handle )
287
- end
288
-
289
- # Returns the total number of changes made to this database instance
290
- # since it was opened.
291
- def total_changes
292
- @driver.total_changes( @handle )
293
- end
294
-
295
- # Interrupts the currently executing operation, causing it to abort.
296
- def interrupt
297
- @driver.interrupt( @handle )
298
- end
299
-
300
- # Register a busy handler with this database instance. When a requested
301
- # resource is busy, this handler will be invoked. If the handler returns
302
- # +false+, the operation will be aborted; otherwise, the resource will
303
- # be requested again.
304
- #
305
- # The handler will be invoked with the name of the resource that was
306
- # busy, and the number of times it has been retried.
307
- #
308
- # See also the mutually exclusive #busy_timeout.
309
- def busy_handler( data=nil, &block ) # :yields: data, retries
310
- result = @driver.busy_handler( @handle, data, &block )
311
- Error.check( result, self )
312
- end
313
-
314
- # Indicates that if a request for a resource terminates because that
315
- # resource is busy, SQLite should sleep and retry for up to the indicated
316
- # number of milliseconds. By default, SQLite does not retry
317
- # busy resources. To restore the default behavior, send 0 as the
318
- # +ms+ parameter.
319
- #
320
- # See also the mutually exclusive #busy_handler.
321
- def busy_timeout( ms )
322
- result = @driver.busy_timeout( @handle, ms )
323
- Error.check( result, self )
324
- end
325
-
326
- # Creates a new function for use in SQL statements. It will be added as
327
- # +name+, with the given +arity+. (For variable arity functions, use
328
- # -1 for the arity.)
329
- #
330
- # The block should accept at least one parameter--the FunctionProxy
331
- # instance that wraps this function invocation--and any other
332
- # arguments it needs (up to its arity).
333
- #
334
- # The block does not return a value directly. Instead, it will invoke
335
- # the FunctionProxy#set_result method on the +func+ parameter and
336
- # indicate the return value that way.
337
- #
338
- # Example:
339
- #
340
- # db.create_function( "maim", 1 ) do |func, value|
341
- # if value.nil?
342
- # func.result = nil
343
- # else
344
- # func.result = value.split(//).sort.join
345
- # end
346
- # end
347
- #
348
- # puts db.get_first_value( "select maim(name) from table" )
349
- def create_function( name, arity, text_rep=Constants::TextRep::ANY,
350
- &block ) # :yields: func, *args
351
- # begin
352
- callback = proc do |func,*args|
353
- begin
354
- block.call( FunctionProxy.new( @driver, func ),
355
- *args.map{|v| Value.new(self,v)} )
356
- rescue StandardError, Exception => e
357
- @driver.result_error( func,
358
- "#{e.message} (#{e.class})", -1 )
359
- end
360
- end
361
-
362
- result = @driver.create_function( @handle, name, arity, text_rep, nil,
363
- callback, nil, nil )
364
- Error.check( result, self )
365
-
366
- self
367
- end
368
-
369
- # Creates a new aggregate function for use in SQL statements. Aggregate
370
- # functions are functions that apply over every row in the result set,
371
- # instead of over just a single row. (A very common aggregate function
372
- # is the "count" function, for determining the number of rows that match
373
- # a query.)
374
- #
375
- # The new function will be added as +name+, with the given +arity+. (For
376
- # variable arity functions, use -1 for the arity.)
377
- #
378
- # The +step+ parameter must be a proc object that accepts as its first
379
- # parameter a FunctionProxy instance (representing the function
380
- # invocation), with any subsequent parameters (up to the function's arity).
381
- # The +step+ callback will be invoked once for each row of the result set.
382
- #
383
- # The +finalize+ parameter must be a +proc+ object that accepts only a
384
- # single parameter, the FunctionProxy instance representing the current
385
- # function invocation. It should invoke FunctionProxy#set_result to
386
- # store the result of the function.
387
- #
388
- # Example:
389
- #
390
- # db.create_aggregate( "lengths", 1 ) do
391
- # step do |func, value|
392
- # func[ :total ] ||= 0
393
- # func[ :total ] += ( value ? value.length : 0 )
394
- # end
395
- #
396
- # finalize do |func|
397
- # func.set_result( func[ :total ] || 0 )
398
- # end
399
- # end
400
- #
401
- # puts db.get_first_value( "select lengths(name) from table" )
402
- #
403
- # See also #create_aggregate_handler for a more object-oriented approach to
404
- # aggregate functions.
405
- def create_aggregate( name, arity, step=nil, finalize=nil,
406
- text_rep=Constants::TextRep::ANY, &block )
407
- # begin
408
- if block
409
- proxy = AggregateDefinitionProxy.new
410
- proxy.instance_eval(&block)
411
- step ||= proxy.step_callback
412
- finalize ||= proxy.finalize_callback
413
- end
414
-
415
- step_callback = proc do |func,*args|
416
- ctx = @driver.aggregate_context( func )
417
- unless ctx[:__error]
418
- begin
419
- step.call( FunctionProxy.new( @driver, func, ctx ),
420
- *args.map{|v| Value.new(self,v)} )
421
- rescue Exception => e
422
- ctx[:__error] = e
423
- end
424
- end
425
- end
426
-
427
- finalize_callback = proc do |func|
428
- ctx = @driver.aggregate_context( func )
429
- unless ctx[:__error]
430
- begin
431
- finalize.call( FunctionProxy.new( @driver, func, ctx ) )
432
- rescue Exception => e
433
- @driver.result_error( func,
434
- "#{e.message} (#{e.class})", -1 )
435
- end
436
- else
437
- e = ctx[:__error]
438
- @driver.result_error( func,
439
- "#{e.message} (#{e.class})", -1 )
440
- end
441
- end
442
-
443
- result = @driver.create_function( @handle, name, arity, text_rep, nil,
444
- nil, step_callback, finalize_callback )
445
- Error.check( result, self )
446
-
447
- self
448
- end
449
-
450
- # This is another approach to creating an aggregate function (see
451
- # #create_aggregate). Instead of explicitly specifying the name,
452
- # callbacks, arity, and type, you specify a factory object
453
- # (the "handler") that knows how to obtain all of that information. The
454
- # handler should respond to the following messages:
455
- #
456
- # +arity+:: corresponds to the +arity+ parameter of #create_aggregate. This
457
- # message is optional, and if the handler does not respond to it,
458
- # the function will have an arity of -1.
459
- # +name+:: this is the name of the function. The handler _must_ implement
460
- # this message.
461
- # +new+:: this must be implemented by the handler. It should return a new
462
- # instance of the object that will handle a specific invocation of
463
- # the function.
464
- #
465
- # The handler instance (the object returned by the +new+ message, described
466
- # above), must respond to the following messages:
467
- #
468
- # +step+:: this is the method that will be called for each step of the
469
- # aggregate function's evaluation. It should implement the same
470
- # signature as the +step+ callback for #create_aggregate.
471
- # +finalize+:: this is the method that will be called to finalize the
472
- # aggregate function's evaluation. It should implement the
473
- # same signature as the +finalize+ callback for
474
- # #create_aggregate.
475
- #
476
- # Example:
477
- #
478
- # class LengthsAggregateHandler
479
- # def self.arity; 1; end
480
- #
481
- # def initialize
482
- # @total = 0
483
- # end
484
- #
485
- # def step( ctx, name )
486
- # @total += ( name ? name.length : 0 )
487
- # end
488
- #
489
- # def finalize( ctx )
490
- # ctx.set_result( @total )
491
- # end
492
- # end
493
- #
494
- # db.create_aggregate_handler( LengthsAggregateHandler )
495
- # puts db.get_first_value( "select lengths(name) from A" )
496
- def create_aggregate_handler( handler )
497
- arity = -1
498
- text_rep = Constants::TextRep::ANY
499
-
500
- arity = handler.arity if handler.respond_to?(:arity)
501
- text_rep = handler.text_rep if handler.respond_to?(:text_rep)
502
- name = handler.name
503
-
504
- step = proc do |func,*args|
505
- ctx = @driver.aggregate_context( func )
506
- unless ctx[ :__error ]
507
- ctx[ :handler ] ||= handler.new
508
- begin
509
- ctx[ :handler ].step( FunctionProxy.new( @driver, func, ctx ),
510
- *args.map{|v| Value.new(self,v)} )
511
- rescue Exception, StandardError => e
512
- ctx[ :__error ] = e
513
- end
514
- end
515
- end
516
-
517
- finalize = proc do |func|
518
- ctx = @driver.aggregate_context( func )
519
- unless ctx[ :__error ]
520
- ctx[ :handler ] ||= handler.new
521
- begin
522
- ctx[ :handler ].finalize( FunctionProxy.new( @driver, func, ctx ) )
523
- rescue Exception => e
524
- ctx[ :__error ] = e
525
- end
526
- end
527
-
528
- if ctx[ :__error ]
529
- e = ctx[ :__error ]
530
- @driver.sqlite3_result_error( func, "#{e.message} (#{e.class})", -1 )
531
- end
532
- end
533
-
534
- result = @driver.create_function( @handle, name, arity, text_rep, nil,
535
- nil, step, finalize )
536
- Error.check( result, self )
537
-
538
- self
539
- end
540
-
541
- # Begins a new transaction. Note that nested transactions are not allowed
542
- # by SQLite, so attempting to nest a transaction will result in a runtime
543
- # exception.
544
- #
545
- # The +mode+ parameter may be either <tt>:deferred</tt> (the default),
546
- # <tt>:immediate</tt>, or <tt>:exclusive</tt>.
547
- #
548
- # If a block is given, the database instance is yielded to it, and the
549
- # transaction is committed when the block terminates. If the block
550
- # raises an exception, a rollback will be performed instead. Note that if
551
- # a block is given, #commit and #rollback should never be called
552
- # explicitly or you'll get an error when the block terminates.
553
- #
554
- # If a block is not given, it is the caller's responsibility to end the
555
- # transaction explicitly, either by calling #commit, or by calling
556
- # #rollback.
557
- def transaction( mode = :deferred )
558
- execute "begin #{mode.to_s} transaction"
559
- @transaction_active = true
560
-
561
- if block_given?
562
- abort = false
563
- begin
564
- yield self
565
- rescue ::Object
566
- abort = true
567
- raise
568
- ensure
569
- abort and rollback or commit
570
- end
571
- end
572
-
573
- true
574
- end
575
-
576
- # Commits the current transaction. If there is no current transaction,
577
- # this will cause an error to be raised. This returns +true+, in order
578
- # to allow it to be used in idioms like
579
- # <tt>abort? and rollback or commit</tt>.
580
- def commit
581
- execute "commit transaction"
582
- @transaction_active = false
583
- true
584
- end
585
-
586
- # Rolls the current transaction back. If there is no current transaction,
587
- # this will cause an error to be raised. This returns +true+, in order
588
- # to allow it to be used in idioms like
589
- # <tt>abort? and rollback or commit</tt>.
590
- def rollback
591
- execute "rollback transaction"
592
- @transaction_active = false
593
- true
594
- end
595
-
596
- # Returns +true+ if there is a transaction active, and +false+ otherwise.
597
- def transaction_active?
598
- @transaction_active
599
- end
600
-
601
- # Loads the corresponding driver, or if it is nil, attempts to locate a
602
- # suitable driver.
603
- def load_driver( driver )
604
- case driver
605
- when Class
606
- # do nothing--use what was given
607
- when Symbol, String
608
- require "sqlite3/driver/#{driver.to_s.downcase}/driver"
609
- driver = SQLite3::Driver.const_get( driver )::Driver
610
- else
611
- [ "Native", "DL" ].each do |d|
612
- begin
613
- require "sqlite3/driver/#{d.downcase}/driver"
614
- driver = SQLite3::Driver.const_get( d )::Driver
615
- break
616
- rescue SyntaxError
617
- raise
618
- rescue ScriptError, Exception, NameError
619
- end
620
- end
621
- raise "no driver for sqlite3 found" unless driver
622
- end
623
-
624
- @driver = driver.new
625
- end
626
- private :load_driver
627
-
628
- # A helper class for dealing with custom functions (see #create_function,
629
- # #create_aggregate, and #create_aggregate_handler). It encapsulates the
630
- # opaque function object that represents the current invocation. It also
631
- # provides more convenient access to the API functions that operate on
632
- # the function object.
633
- #
634
- # This class will almost _always_ be instantiated indirectly, by working
635
- # with the create methods mentioned above.
636
- class FunctionProxy
637
-
638
- # Create a new FunctionProxy that encapsulates the given +func+ object.
639
- # If context is non-nil, the functions context will be set to that. If
640
- # it is non-nil, it must quack like a Hash. If it is nil, then none of
641
- # the context functions will be available.
642
- def initialize( driver, func, context=nil )
643
- @driver = driver
644
- @func = func
645
- @context = context
646
- end
647
-
648
- # Calls #set_result to set the result of this function.
649
- def result=( result )
650
- set_result( result )
651
- end
652
-
653
- # Set the result of the function to the given value. The function will
654
- # then return this value.
655
- def set_result( result, utf16=false )
656
- @driver.result_text( @func, result, utf16 )
657
- end
658
-
659
- # Set the result of the function to the given error message.
660
- # The function will then return that error.
661
- def set_error( error )
662
- @driver.result_error( @func, error.to_s, -1 )
663
- end
664
-
665
- # (Only available to aggregate functions.) Returns the number of rows
666
- # that the aggregate has processed so far. This will include the current
667
- # row, and so will always return at least 1.
668
- def count
669
- ensure_aggregate!
670
- @driver.aggregate_count( @func )
671
- end
672
-
673
- # Returns the value with the given key from the context. This is only
674
- # available to aggregate functions.
675
- def []( key )
676
- ensure_aggregate!
677
- @context[ key ]
678
- end
679
-
680
- # Sets the value with the given key in the context. This is only
681
- # available to aggregate functions.
682
- def []=( key, value )
683
- ensure_aggregate!
684
- @context[ key ] = value
685
- end
686
-
687
- # A function for performing a sanity check, to ensure that the function
688
- # being invoked is an aggregate function. This is implied by the
689
- # existence of the context variable.
690
- def ensure_aggregate!
691
- unless @context
692
- raise MisuseException, "function is not an aggregate"
693
- end
694
- end
695
- private :ensure_aggregate!
696
-
697
- end
698
-
699
- # A proxy used for defining the callbacks to an aggregate function.
700
- class AggregateDefinitionProxy # :nodoc:
701
- attr_reader :step_callback, :finalize_callback
702
-
703
- def step( &block )
704
- @step_callback = block
705
- end
706
-
707
- def finalize( &block )
708
- @finalize_callback = block
709
- end
710
- end
711
-
712
- end
713
-
714
- end
715
-