SqlStatement 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/doc/ChangeLog ADDED
@@ -0,0 +1,14 @@
1
+ =Change Log
2
+ ==1.0.1
3
+ * +add_fields+ and +add_tables+ now support hashes too, so no more of
4
+ this silly <tt>stmt.fields.merge!</tt> stuff is requred anymore
5
+ * now there's a DBI adaptor that lets DBI accept +SQLStatements+ and
6
+ process them automatically, so no more of this silly
7
+ <tt>dbh.do(stmt.to_s,*stmt.placeheld)</tt> you can just say
8
+ <tt>dbh.do(stmt)</tt> (likewise for +execute+)
9
+ * fix a bug in string_func which broke it completely.
10
+ * Documentation fixes
11
+ * Make <tt>== nil</tt> comparisons turn into <tt>is null</tt> in SQL.
12
+
13
+ ==1.0.0
14
+ Initial Release
data/doc/EXAMPLE ADDED
@@ -0,0 +1,124 @@
1
+ #=Example
2
+ # # We subclass the standard classes so that we can construct
3
+ # # a query with a complex computed field. The newlists method
4
+ # # makes this easy, giving us a constructor and combination
5
+ # # operators for free.
6
+ #
7
+ # class ProbabilityParts < SQLStatement::SelectParts
8
+ # newlists :multiply
9
+ # end
10
+ # class ProbabilityStatement < SQLStatement::SelectCreate
11
+ # newlists :multiply
12
+ #
13
+ # # Add our own special fields here, computed
14
+ # # from the list we added to this class.
15
+ # def allfields
16
+ # retval=super
17
+ # retval.merge! :probability =>
18
+ # SQLFunc(@multiply.collect{|x| x.to_sqlpart}.join('*'))
19
+ # retval
20
+ # end
21
+ # end
22
+ #
23
+ #
24
+ # #In real life, I use a special library for detecting what attributes
25
+ # #are available based on the columns in my tables.
26
+ # #We'll mimic that functionality here.
27
+ # #
28
+ # #The first definition of this module mocks the stuff that's in my more
29
+ # #complicated library
30
+ # module AttributeDetection
31
+ # class Attribute
32
+ # def initialize(domain,name)
33
+ # @domain=domain
34
+ # @name=name
35
+ # end
36
+ # attr_reader :name, :domain
37
+ # end
38
+ # class Hierarchy < Attribute
39
+ # def initialize(leftcolumn)
40
+ # ignored1,m_domain,m_name,ignored2=leftcolumn.split(/_/)
41
+ # super(m_domain,m_name)
42
+ # end
43
+ # end
44
+ # class DomainlessPerGroup < Attribute
45
+ # def initialize (colname)
46
+ # name=@colname=colname
47
+ # domain="!"
48
+ # super(domain,name)
49
+ # end
50
+ # end
51
+ #
52
+ # #I have a few other attribute types, none of which
53
+ # #are used in this code yet.
54
+ #
55
+ # end
56
+ #
57
+ # include SQLHelpers
58
+ #
59
+ # #The second definition of this module is the code that I actually define
60
+ # #in this particular program.
61
+ # module AttributeDetection
62
+ # class Attribute
63
+ # def condname
64
+ # :"cond_#{name}"
65
+ # end
66
+ # def priorname
67
+ # :"prior_#{name}"
68
+ # end
69
+ # #parts that go in to the CREATE TABLE `probabilities` statement
70
+ # #which relate to the attribute we are disambiguating
71
+ # def probability_disambig
72
+ # parts=ProbabilityParts.new
73
+ # parts.add_fields [priorname[probkey]]
74
+ # parts.add_tables [priorname]
75
+ # parts.multiply << priorname[:pt]
76
+ # parts
77
+ # end
78
+ # #parts that go in to the CREATE TABLE `probabilities` statement
79
+ # #which relate to the attributes we are using as features
80
+ # def probability_features(conditionalon)
81
+ # parts=ProbabilityParts.new
82
+ # parts.tables={priorname=>priorname,condname=>condname}
83
+ # parts.add_fields [priorname[probkey]]
84
+ # parts.conditions << sql_func{ condname[probkey]==priorname[probkey]}
85
+ # parts.conditions << sql_func{
86
+ # condname[conditionalon.probkey]==
87
+ # conditionalon.priorname[conditionalon.probkey]
88
+ # }
89
+ # parts.multiply << sql_func{condname[:pt]/priorname[:pt]}
90
+ # parts
91
+ # end
92
+ # end
93
+ # class Hierarchy
94
+ # def probkey
95
+ # "att_#{domain}_#{name}_left".to_sym
96
+ # end
97
+ # end
98
+ # class DomainlessPerGroup
99
+ # def probkey
100
+ # @colname.to_sym
101
+ # end
102
+ # end
103
+ # end
104
+ #
105
+ # include AttributeDetection
106
+ # include SQLHelpers
107
+ #
108
+ # #And this code calls methods to actually build the query.
109
+ #
110
+ # #First we define the names of the attributes
111
+ # att_disambig=Hierarchy.new("att_appraisal_attitude_left")
112
+ # att_features=[Hierarchy.new("att_products_appraisedtype_left"),
113
+ # DomainlessPerGroup.new("priority")]
114
+ #
115
+ #
116
+ # #Then we construct the statement
117
+ # stmt=ProbabilityStatement.new :probabilities,true
118
+ #
119
+ # stmt << att_disambig.probability_disambig
120
+ # att_features.each { |x| stmt<< x.probability_features(att_disambig) }
121
+ #
122
+ # #Finally, we execute it against the database. Assume dbh is a
123
+ # #dbi connection handle.
124
+ # dbh.execute(stmt.to_s,*stmt.placeheld)
@@ -0,0 +1,25 @@
1
+ #Redefine DBI's query methods to accept SQL statement objects from
2
+ #this library. This is automatically loaded if DBI was loaded before
3
+ #+sqlstatement+. If not, <tt>require 'sql/dbi-support'</tt> after DBI
4
+ #is included and you'll be on your way.
5
+ class DBI::DatabaseHandle
6
+ alias_method :kenbloom_sqlstatement_old_do, :"do"
7
+ alias_method :kenbloom_sqlstatement_old_execute, :execute
8
+
9
+ def do(stmt,*args)
10
+ if stmt.respond_to?(:placeheld)
11
+ kenbloom_sqlstatement_old_do(stmt.to_s,*stmt.placeheld)
12
+ else
13
+ kenbloom_sqlstatement_old_do(stmt,*args)
14
+ end
15
+ end
16
+ def execute(stmt,*args)
17
+ if stmt.respond_to?(:placeheld)
18
+ kenbloom_sqlstatement_old_execute(stmt.to_s,*stmt.placeheld)
19
+ else
20
+ kenbloom_sqlstatement_old_execute(stmt,*args)
21
+ end
22
+ end
23
+
24
+ end
25
+
@@ -12,6 +12,10 @@ class Array
12
12
  "count(distinct "+rest_sqlpart+")"
13
13
  elsif first.to_s =~ /[a-zA-Z]/
14
14
  first.to_s+"("+rest_sqlpart+")"
15
+ elsif first == :== and self[2]==nil
16
+ self[1].to_sqlpart+" is null"
17
+ elsif first == :== and self[1]==nil
18
+ self[2].to_sqlpart+" is null"
15
19
  elsif x={:& => " and ", :| => " or ", :== => "="}[first]
16
20
  "("+self[1].to_sqlpart+x+self[2].to_sqlpart+")"
17
21
  else
data/lib/sql/statement.rb CHANGED
@@ -1,70 +1,3 @@
1
- =begin rdoc
2
- = sqlstatement - Generate complex SQL statements programmatically
3
-
4
- The main goal of this library is to be able to construct an SQL statement
5
- from "slices" that concern different aspects of the final query (perhaps
6
- in different places in your code) and then combine them all together into
7
- one statement easily.
8
-
9
- Another important goal of this library is to give some consistent Ruby
10
- syntax to three statements (INSERT, SELECT, and UPDATE) that seem to have
11
- different enough syntax that one has two write different code to generate
12
- each kind of statement.
13
-
14
- I use my SQL database (specifically MySQL) largely as a bulk data
15
- processing engine, by doing INSERT...SELECT or CREATE TABLE...SELECT
16
- statements. This library is intended to make that kind of coding easier. I
17
- expect that Object Relational mappers (such as ActiveRecord) are more
18
- useful for most people, who are performing queries and
19
- inserting/updating/querying for individual records. I have nevertheless
20
- added INSERT...VALUES statements, and will add other statements soon, for
21
- consistency.
22
-
23
- This library is inspired by CLSQL[http://clsql.b9.com/] for Common LISP,
24
- or SchemeQL[http://schematics.sourceforge.net/schemeql.html] for Scheme,
25
- although it is very different from these two libraries. Scheme and
26
- LISP's use of s-expressions make it very easy to construct an entire
27
- sublanguage for the WHERE clause, simply by list parsing. The
28
- Criteria[http://mephle.org/Criteria/] library for Ruby has attempted
29
- this, but in a more limited manner than SchemeQL or CLSQL. My library
30
- aims to cover much of the functionality in these libraries.
31
-
32
- This library doesn't try to abstract out the limitations of your DBMS, and
33
- I think that the SQL it uses should be fairly portable, in large measure
34
- because it hasn't attempted to deal with serious CREATE TABLE statements,
35
- where a lot of syntax concerning types, keys and sequences is much more
36
- variable.
37
-
38
- ==License
39
-
40
- Copyright (c) 2006 Ken Bloom
41
- All rights reserved.
42
-
43
- Redistribution and use in source and binary forms, with or without
44
- modification, are permitted provided that the following conditions
45
- are met:
46
- 1. Redistributions of source code must retain the above copyright
47
- notice, this list of conditions and the following disclaimer.
48
- 2. Redistributions in binary form must reproduce the above copyright
49
- notice, this list of conditions and the following disclaimer in the
50
- documentation and/or other materials provided with the distribution.
51
- 3. Neither Ken Bloom's name, nor the name of any of his contributors may
52
- may be used to endorse or promote products derived from this software
53
- without specific prior written permission.
54
-
55
- THIS SOFTWARE IS PROVIDED BY THE KEN BLOOM AND CONTRIBUTORS ``AS IS'' AND
56
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58
- ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65
- SUCH DAMAGE.
66
- =end
67
-
68
1
  # Everything in Ruby is a descendant of the Object class. Practically
69
2
  # speaking, this means that any object that hasn't otherwise defined
70
3
  # the +placeheld+ and <tt>to_sqlpart</tt> methods will be treated as a piece
@@ -107,6 +40,15 @@ class Symbol
107
40
  end
108
41
  end
109
42
 
43
+ class NilClass
44
+ def to_sqlpart
45
+ "null"
46
+ end
47
+ def placeheld
48
+ []
49
+ end
50
+ end
51
+
110
52
  module SQLStatement
111
53
 
112
54
  # This class is used to represent complex SQL expressions, so that
@@ -118,7 +60,7 @@ module SQLStatement
118
60
  class Function < String
119
61
 
120
62
  def initialize(val="",placeheld=[])
121
- super
63
+ super val
122
64
  @placeheld=placeheld
123
65
  end
124
66
 
@@ -220,19 +162,24 @@ class SelectParts
220
162
  #See the documentation for SelectStatement which describes these.
221
163
  attr_accessor :conditions, :groupby, :orderby, :fields, :tables
222
164
 
223
- #Adds an array of unaliased fields to this SQL statement. See
224
- #documentation at SelectStatement
165
+ #Adds an array of unaliased fields, or a hash of aliased fields to
166
+ #this SQL statement. See documentation at SelectStatement
225
167
  def add_fields (newfields)
226
- newfields.each do |x|
227
- @fields[x]=x
168
+ if newfields.is_a?(Hash)
169
+ @fields.merge! newfields
170
+ else
171
+ newfields.each { |x| @fields[x]=x }
228
172
  end
173
+ nil
229
174
  end
230
175
 
231
- #Adds an array of unaliased tables to this SQL statement. See
232
- #documentation for SelectStatement
176
+ #Adds an array of unaliased tables or a hash of aliased tables to
177
+ #this SQL statement. See documentation for SelectStatement
233
178
  def add_tables (newtables)
234
- newtables.each do |x|
235
- @tables[x]=x
179
+ if newtables.is_a?(Hash)
180
+ @tables.merge! newtables
181
+ else
182
+ newtables.each { |x| @tables[x]=x }
236
183
  end
237
184
  end
238
185
 
@@ -297,11 +244,15 @@ class Select
297
244
  #aliasing a field name, use the form <tt>fieldname => fieldname</tt>.
298
245
  attr_accessor :fields
299
246
 
300
- #Adds an array of unaliased fields to this SQL statement.
247
+ #Adds an array of unaliased fields, or a hash of aliased fields to
248
+ #this SQL statement.
301
249
  def add_fields (newfields)
302
- newfields.each do |x|
303
- @fields[x]=x
250
+ if newfields.is_a?(Hash)
251
+ @fields.merge! newfields
252
+ else
253
+ newfields.each { |x| @fields[x]=x }
304
254
  end
255
+ nil
305
256
  end
306
257
 
307
258
  #This is the tables to include in the query (i.e. the +FROM+ clause).
@@ -311,10 +262,13 @@ class Select
311
262
  #aliasing a table name, use the form <tt>tablename => tablename</tt>.
312
263
  attr_accessor :tables
313
264
 
314
- #Adds an array of unaliased tables to this SQL statement.
265
+ #Adds an array of unaliased tables or a hash of aliased tables to
266
+ #this SQL statement.
315
267
  def add_tables (newtables)
316
- newtables.each do |x|
317
- @tables[x]=x
268
+ if newtables.is_a?(Hash)
269
+ @tables.merge! newtables
270
+ else
271
+ newtables.each { |x| @tables[x]=x }
318
272
  end
319
273
  end
320
274
 
data/lib/sqlstatement.rb CHANGED
@@ -1,2 +1,81 @@
1
+ =begin rdoc
2
+ = sqlstatement - Generate complex SQL statements programmatically
3
+
4
+ The main goal of this library is to be able to construct an SQL statement
5
+ from "slices" that concern different aspects of the final query (perhaps
6
+ in different places in your code) and then combine them all together into
7
+ one statement easily.
8
+
9
+ Another important goal of this library is to give some consistent Ruby
10
+ syntax to three statements (INSERT, SELECT, and UPDATE) that seem to have
11
+ different enough syntax that one has two write different code to generate
12
+ each kind of statement.
13
+
14
+ I use my SQL database (specifically MySQL) largely as a bulk data
15
+ processing engine, by doing INSERT...SELECT or CREATE TABLE...SELECT
16
+ statements. This library is intended to make that kind of coding easier. I
17
+ expect that Object Relational mappers (such as ActiveRecord) are more
18
+ useful for most people, who are performing queries and
19
+ inserting/updating/querying for individual records. I have nevertheless
20
+ added INSERT...VALUES statements, and will add other statements soon, for
21
+ consistency.
22
+
23
+ This library is inspired by CLSQL[http://clsql.b9.com/] for Common LISP,
24
+ or SchemeQL[http://schematics.sourceforge.net/schemeql.html] for Scheme,
25
+ although it is very different from these two libraries. Scheme and
26
+ LISP's use of s-expressions make it very easy to construct an entire
27
+ sublanguage for the WHERE clause, simply by list parsing. The
28
+ Criteria[http://mephle.org/Criteria/] library for Ruby has attempted
29
+ this, but in a more limited manner than SchemeQL or CLSQL. My library
30
+ aims to cover much of the functionality in these libraries.
31
+
32
+ This library doesn't try to abstract out the limitations of your DBMS, and
33
+ I think that the SQL it uses should be fairly portable, in large measure
34
+ because it hasn't attempted to deal with serious CREATE TABLE statements,
35
+ where a lot of syntax concerning types, keys and sequences is much more
36
+ variable.
37
+
38
+ This library can be downloaded from
39
+ http://rubyforge.org/projects/sqlstatement/
40
+
41
+ ==Author
42
+ Ken Bloom <mailto:kbloom@gmail.com>
43
+
44
+ http://www.iit.edu/~kbloom1/
45
+
46
+ ==License
47
+
48
+ Copyright (c) 2006 Ken Bloom
49
+ All rights reserved.
50
+
51
+ Redistribution and use in source and binary forms, with or without
52
+ modification, are permitted provided that the following conditions
53
+ are met:
54
+ 1. Redistributions of source code must retain the above copyright
55
+ notice, this list of conditions and the following disclaimer.
56
+ 2. Redistributions in binary form must reproduce the above copyright
57
+ notice, this list of conditions and the following disclaimer in the
58
+ documentation and/or other materials provided with the distribution.
59
+ 3. Neither Ken Bloom's name, nor the name of any of his contributors may
60
+ may be used to endorse or promote products derived from this software
61
+ without specific prior written permission.
62
+
63
+ THIS SOFTWARE IS PROVIDED BY THE KEN BLOOM AND CONTRIBUTORS ``AS IS'' AND
64
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
65
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
66
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
67
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
68
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
69
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
71
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
72
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
73
+ SUCH DAMAGE.
74
+ =end
75
+
76
+
1
77
  require 'sql/statement'
2
78
  require 'sql/expression'
79
+ if defined?(DBI)
80
+ require 'sql/dbi-support'
81
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: SqlStatement
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.0.0
7
- date: 2006-10-09 00:00:00 -05:00
6
+ version: 1.0.1
7
+ date: 2006-11-20 00:00:00 -06:00
8
8
  summary: A library for generating arbitrary SQL statements using convenient Ruby objects.
9
9
  require_paths:
10
10
  - lib
@@ -29,18 +29,22 @@ post_install_message:
29
29
  authors:
30
30
  - Ken Bloom
31
31
  files:
32
- - bin/example.rb
33
32
  - lib/sql
34
33
  - lib/sqlstatement.rb
35
34
  - lib/sql/expression.rb
36
35
  - lib/sql/bathon-sxp.rb
37
36
  - lib/sql/statement.rb
37
+ - lib/sql/dbi-support.rb
38
+ - doc/EXAMPLE
39
+ - doc/ChangeLog
38
40
  test_files: []
39
41
 
40
- rdoc_options: []
41
-
42
- extra_rdoc_files: []
43
-
42
+ rdoc_options:
43
+ - --main
44
+ - lib/sqlstatement.rb
45
+ extra_rdoc_files:
46
+ - doc/EXAMPLE
47
+ - doc/ChangeLog
44
48
  executables: []
45
49
 
46
50
  extensions: []
data/bin/example.rb DELETED
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require_gem 'SqlStatement'
3
- require 'sql/statement'
4
- require 'sql/expression'
5
-
6
- # We subclass the standard classes so that we can construct
7
- # a query with a complex computed field. The newlists method
8
- # makes this easy, giving us a constructor and combination
9
- # operators for free.
10
-
11
- class ProbabilityParts < SQLStatement::SelectParts
12
- newlists :multiply
13
- end
14
- class ProbabilityStatement < SQLStatement::SelectCreate
15
- newlists :multiply
16
-
17
- # Add our own special fields here, computed
18
- # from the list we added to this class.
19
- def allfields
20
- retval=super
21
- retval.merge! :probability =>
22
- SQLFunc(@multiply.collect{|x| x.to_sqlpart}.join('*'))
23
- retval
24
- end
25
- end
26
-
27
-
28
- #In real life, I use a special library for detecting what attributes
29
- #are available based on the columns in my tables.
30
- #We'll mimic that functionality here.
31
- #
32
- #The first definition of this module mocks the stuff that's in my more
33
- #complicated library
34
- module AttributeDetection
35
- class Attribute
36
- def initialize(domain,name)
37
- @domain=domain
38
- @name=name
39
- end
40
- attr_reader :name, :domain
41
- end
42
- class Hierarchy < Attribute
43
- def initialize(leftcolumn)
44
- ignored1,m_domain,m_name,ignored2=leftcolumn.split(/_/)
45
- super(m_domain,m_name)
46
- end
47
- end
48
- class DomainlessPerGroup < Attribute
49
- def initialize (colname)
50
- name=@colname=colname
51
- domain="!"
52
- super(domain,name)
53
- end
54
- end
55
-
56
- #I have a few other attribute types, none of which
57
- #are used in this code yet.
58
-
59
- end
60
-
61
- include SQLHelpers
62
-
63
- #The second definition of this module is the code that I actually define
64
- #in this particular program.
65
- module AttributeDetection
66
- class Attribute
67
- def condname
68
- :"cond_#{name}"
69
- end
70
- def priorname
71
- :"prior_#{name}"
72
- end
73
- #parts that go in to the CREATE TABLE `probabilities` statement
74
- #which relate to the attribute we are disambiguating
75
- def probability_disambig
76
- parts=ProbabilityParts.new
77
- parts.add_fields [priorname[probkey]]
78
- parts.add_tables [priorname]
79
- parts.multiply << priorname[:pt]
80
- parts
81
- end
82
- #parts that go in to the CREATE TABLE `probabilities` statement
83
- #which relate to the attributes we are using as features
84
- def probability_features(conditionalon)
85
- parts=ProbabilityParts.new
86
- parts.tables={priorname=>priorname,condname=>condname}
87
- parts.add_fields [priorname[probkey]]
88
- parts.conditions << sql_func{ condname[probkey]==priorname[probkey]}
89
- parts.conditions << sql_func{
90
- condname[conditionalon.probkey]==
91
- conditionalon.priorname[conditionalon.probkey]
92
- }
93
- parts.multiply << sql_func{condname[:pt]/priorname[:pt]}
94
- parts
95
- end
96
- end
97
- class Hierarchy
98
- def probkey
99
- "att_#{domain}_#{name}_left".to_sym
100
- end
101
- end
102
- class DomainlessPerGroup
103
- def probkey
104
- @colname.to_sym
105
- end
106
- end
107
- end
108
-
109
- include AttributeDetection
110
- include SQLHelpers
111
-
112
- #And this code calls methods to actually build the query.
113
-
114
- #First we define the names of the attributes
115
- att_disambig=Hierarchy.new("att_appraisal_attitude_left")
116
- att_features=[Hierarchy.new("att_products_appraisedtype_left"),
117
- DomainlessPerGroup.new("priority")]
118
-
119
-
120
- #Then we construct the statement
121
- stmt=ProbabilityStatement.new :probabilities,true
122
-
123
- stmt << att_disambig.probability_disambig
124
- att_features.each { |x| stmt<< x.probability_features(att_disambig) }
125
-
126
- #Finally, we print it (or execute it against the database)
127
- puts stmt.to_s
128
-
129
-