user_query 0.1.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.
- data/ChangeLog +4 -0
- data/README +45 -0
- data/Rakefile +359 -0
- data/Releases +6 -0
- data/TODO +0 -0
- data/examples/userqueryex/HOWTO.txt +5 -0
- data/examples/userqueryex/README +183 -0
- data/examples/userqueryex/Rakefile +10 -0
- data/examples/userqueryex/WHAT.txt +16 -0
- data/examples/userqueryex/app/controllers/application.rb +4 -0
- data/examples/userqueryex/app/controllers/entries_controller.rb +68 -0
- data/examples/userqueryex/app/helpers/application_helper.rb +3 -0
- data/examples/userqueryex/app/helpers/entries_helper.rb +2 -0
- data/examples/userqueryex/app/models/entry.rb +8 -0
- data/examples/userqueryex/app/views/entries/_form.rhtml +20 -0
- data/examples/userqueryex/app/views/entries/edit.rhtml +9 -0
- data/examples/userqueryex/app/views/entries/list.rhtml +75 -0
- data/examples/userqueryex/app/views/entries/new.rhtml +8 -0
- data/examples/userqueryex/app/views/entries/show.rhtml +8 -0
- data/examples/userqueryex/app/views/layouts/entries.rhtml +13 -0
- data/examples/userqueryex/config/boot.rb +44 -0
- data/examples/userqueryex/config/database.yml +36 -0
- data/examples/userqueryex/config/environment.rb +54 -0
- data/examples/userqueryex/config/environments/development.rb +21 -0
- data/examples/userqueryex/config/environments/production.rb +18 -0
- data/examples/userqueryex/config/environments/test.rb +19 -0
- data/examples/userqueryex/config/routes.rb +22 -0
- data/examples/userqueryex/db/migrate/001_entry_migration.rb +16 -0
- data/examples/userqueryex/db/schema.rb +15 -0
- data/examples/userqueryex/doc/README_FOR_APP +2 -0
- data/examples/userqueryex/public/404.html +8 -0
- data/examples/userqueryex/public/500.html +8 -0
- data/examples/userqueryex/public/dispatch.cgi +10 -0
- data/examples/userqueryex/public/dispatch.fcgi +24 -0
- data/examples/userqueryex/public/dispatch.rb +10 -0
- data/examples/userqueryex/public/favicon.ico +0 -0
- data/examples/userqueryex/public/images/rails.png +0 -0
- data/examples/userqueryex/public/javascripts/application.js +2 -0
- data/examples/userqueryex/public/javascripts/controls.js +815 -0
- data/examples/userqueryex/public/javascripts/dragdrop.js +913 -0
- data/examples/userqueryex/public/javascripts/effects.js +958 -0
- data/examples/userqueryex/public/javascripts/prototype.js +2006 -0
- data/examples/userqueryex/public/robots.txt +1 -0
- data/examples/userqueryex/public/stylesheets/scaffold.css +74 -0
- data/examples/userqueryex/script/about +3 -0
- data/examples/userqueryex/script/breakpointer +3 -0
- data/examples/userqueryex/script/console +3 -0
- data/examples/userqueryex/script/destroy +3 -0
- data/examples/userqueryex/script/generate +3 -0
- data/examples/userqueryex/script/performance/benchmarker +3 -0
- data/examples/userqueryex/script/performance/profiler +3 -0
- data/examples/userqueryex/script/plugin +3 -0
- data/examples/userqueryex/script/process/reaper +3 -0
- data/examples/userqueryex/script/process/spawner +3 -0
- data/examples/userqueryex/script/runner +3 -0
- data/examples/userqueryex/script/server +3 -0
- data/examples/userqueryex/test/fixtures/entries.yml +5 -0
- data/examples/userqueryex/test/functional/entries_controller_test.rb +88 -0
- data/examples/userqueryex/test/test_helper.rb +28 -0
- data/examples/userqueryex/test/unit/entry_test.rb +10 -0
- data/lib/user_query.rb +10 -0
- data/lib/user_query/generator.rb +219 -0
- data/lib/user_query/parameters.rb +93 -0
- data/lib/user_query/parser.rb +762 -0
- data/lib/user_query/schema.rb +159 -0
- data/lib/user_query/user_query_version.rb +6 -0
- data/test/parser_test.rb +539 -0
- data/test/schema_test.rb +142 -0
- metadata +148 -0
@@ -0,0 +1 @@
|
|
1
|
+
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
|
@@ -0,0 +1,74 @@
|
|
1
|
+
body { background-color: #fff; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 13px;
|
6
|
+
line-height: 18px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 11px;
|
13
|
+
}
|
14
|
+
|
15
|
+
a { color: #000; }
|
16
|
+
a:visited { color: #666; }
|
17
|
+
a:hover { color: #fff; background-color:#000; }
|
18
|
+
|
19
|
+
.fieldWithErrors {
|
20
|
+
padding: 2px;
|
21
|
+
background-color: red;
|
22
|
+
display: table;
|
23
|
+
}
|
24
|
+
|
25
|
+
#errorExplanation {
|
26
|
+
width: 400px;
|
27
|
+
border: 2px solid red;
|
28
|
+
padding: 7px;
|
29
|
+
padding-bottom: 12px;
|
30
|
+
margin-bottom: 20px;
|
31
|
+
background-color: #f0f0f0;
|
32
|
+
}
|
33
|
+
|
34
|
+
#errorExplanation h2 {
|
35
|
+
text-align: left;
|
36
|
+
font-weight: bold;
|
37
|
+
padding: 5px 5px 5px 15px;
|
38
|
+
font-size: 12px;
|
39
|
+
margin: -7px;
|
40
|
+
background-color: #c00;
|
41
|
+
color: #fff;
|
42
|
+
}
|
43
|
+
|
44
|
+
#errorExplanation p {
|
45
|
+
color: #333;
|
46
|
+
margin-bottom: 0;
|
47
|
+
padding: 5px;
|
48
|
+
}
|
49
|
+
|
50
|
+
#errorExplanation ul li {
|
51
|
+
font-size: 12px;
|
52
|
+
list-style: square;
|
53
|
+
}
|
54
|
+
|
55
|
+
div.uploadStatus {
|
56
|
+
margin: 5px;
|
57
|
+
}
|
58
|
+
|
59
|
+
div.progressBar {
|
60
|
+
margin: 5px;
|
61
|
+
}
|
62
|
+
|
63
|
+
div.progressBar div.border {
|
64
|
+
background-color: #fff;
|
65
|
+
border: 1px solid grey;
|
66
|
+
width: 100%;
|
67
|
+
}
|
68
|
+
|
69
|
+
div.progressBar div.background {
|
70
|
+
background-color: #333;
|
71
|
+
height: 18px;
|
72
|
+
width: 0%;
|
73
|
+
}
|
74
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
require 'entries_controller'
|
3
|
+
|
4
|
+
# Re-raise errors caught by the controller.
|
5
|
+
class EntriesController; def rescue_action(e) raise e end; end
|
6
|
+
|
7
|
+
class EntriesControllerTest < Test::Unit::TestCase
|
8
|
+
fixtures :entries
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@controller = EntriesController.new
|
12
|
+
@request = ActionController::TestRequest.new
|
13
|
+
@response = ActionController::TestResponse.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_index
|
17
|
+
get :index
|
18
|
+
assert_response :success
|
19
|
+
assert_template 'list'
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_list
|
23
|
+
get :list
|
24
|
+
|
25
|
+
assert_response :success
|
26
|
+
assert_template 'list'
|
27
|
+
|
28
|
+
assert_not_nil assigns(:entries)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_show
|
32
|
+
get :show, :id => 1
|
33
|
+
|
34
|
+
assert_response :success
|
35
|
+
assert_template 'show'
|
36
|
+
|
37
|
+
assert_not_nil assigns(:entry)
|
38
|
+
assert assigns(:entry).valid?
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_new
|
42
|
+
get :new
|
43
|
+
|
44
|
+
assert_response :success
|
45
|
+
assert_template 'new'
|
46
|
+
|
47
|
+
assert_not_nil assigns(:entry)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_create
|
51
|
+
num_entries = Entry.count
|
52
|
+
|
53
|
+
post :create, :entry => {}
|
54
|
+
|
55
|
+
assert_response :redirect
|
56
|
+
assert_redirected_to :action => 'list'
|
57
|
+
|
58
|
+
assert_equal num_entries + 1, Entry.count
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_edit
|
62
|
+
get :edit, :id => 1
|
63
|
+
|
64
|
+
assert_response :success
|
65
|
+
assert_template 'edit'
|
66
|
+
|
67
|
+
assert_not_nil assigns(:entry)
|
68
|
+
assert assigns(:entry).valid?
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_update
|
72
|
+
post :update, :id => 1
|
73
|
+
assert_response :redirect
|
74
|
+
assert_redirected_to :action => 'show', :id => 1
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_destroy
|
78
|
+
assert_not_nil Entry.find(1)
|
79
|
+
|
80
|
+
post :destroy, :id => 1
|
81
|
+
assert_response :redirect
|
82
|
+
assert_redirected_to :action => 'list'
|
83
|
+
|
84
|
+
assert_raise(ActiveRecord::RecordNotFound) {
|
85
|
+
Entry.find(1)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
|
3
|
+
require 'test_help'
|
4
|
+
|
5
|
+
class Test::Unit::TestCase
|
6
|
+
# Transactional fixtures accelerate your tests by wrapping each test method
|
7
|
+
# in a transaction that's rolled back on completion. This ensures that the
|
8
|
+
# test database remains unchanged so your fixtures don't have to be reloaded
|
9
|
+
# between every test method. Fewer database queries means faster tests.
|
10
|
+
#
|
11
|
+
# Read Mike Clark's excellent walkthrough at
|
12
|
+
# http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting
|
13
|
+
#
|
14
|
+
# Every Active Record database supports transactions except MyISAM tables
|
15
|
+
# in MySQL. Turn off transactional fixtures in this case; however, if you
|
16
|
+
# don't care one way or the other, switching from MyISAM to InnoDB tables
|
17
|
+
# is recommended.
|
18
|
+
self.use_transactional_fixtures = true
|
19
|
+
|
20
|
+
# Instantiated fixtures are slow, but give you @david where otherwise you
|
21
|
+
# would need people(:david). If you don't want to migrate your existing
|
22
|
+
# test cases which use the @david style and don't mind the speed hit (each
|
23
|
+
# instantiated fixtures translates to a database query per test method),
|
24
|
+
# then set this back to true.
|
25
|
+
self.use_instantiated_fixtures = false
|
26
|
+
|
27
|
+
# Add more helper methods to be used by all tests here...
|
28
|
+
end
|
data/lib/user_query.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
require 'user_query/user_query_version.rb'
|
6
|
+
require 'user_query/parameters.rb'
|
7
|
+
require 'user_query/parser.rb'
|
8
|
+
require 'user_query/generator.rb'
|
9
|
+
require 'user_query/schema.rb'
|
10
|
+
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module UserQuery
|
2
|
+
|
3
|
+
class Generator
|
4
|
+
class Error < Exception; end
|
5
|
+
|
6
|
+
# Inputs
|
7
|
+
attr_accessor :type
|
8
|
+
attr_accessor :values_inline
|
9
|
+
attr_accessor :target
|
10
|
+
attr_accessor :verbose
|
11
|
+
|
12
|
+
# Outputs
|
13
|
+
attr_accessor :expr
|
14
|
+
attr_accessor :values
|
15
|
+
|
16
|
+
def initialize(*opts)
|
17
|
+
self.target = '<<TARGET>>'
|
18
|
+
self.values_inline = true
|
19
|
+
self.type = :string
|
20
|
+
opts = Hash[*opts]
|
21
|
+
opts.each{|k, v| self.send("#{k}=", v)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def sql(expr)
|
25
|
+
@values = [ ]
|
26
|
+
@expr = ''
|
27
|
+
|
28
|
+
return nil if expr.nil?
|
29
|
+
|
30
|
+
emit_sql(expr)
|
31
|
+
|
32
|
+
@expr
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
@@empty_hash = { }
|
38
|
+
@@empty_hash.freeze
|
39
|
+
|
40
|
+
@@OP_2_SQL = {
|
41
|
+
true => {
|
42
|
+
:not => 'NOT',
|
43
|
+
:and => 'AND',
|
44
|
+
:or => 'OR',
|
45
|
+
:lt => '<',
|
46
|
+
:gt => '>',
|
47
|
+
:le => '<=',
|
48
|
+
:ge => '>=',
|
49
|
+
:eq => '=',
|
50
|
+
:ne => '<>'
|
51
|
+
},
|
52
|
+
:null => {
|
53
|
+
:eq => 'IS',
|
54
|
+
:ne => 'IS NOT'
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
def op_to_sql(type, expr = nil)
|
59
|
+
#if true || @verbose
|
60
|
+
# $stderr.puts "opt_to_sql(#{type.inspect}, #{expr.inspect}) => "
|
61
|
+
#end
|
62
|
+
|
63
|
+
expr = expr && expr[0]
|
64
|
+
sql = nil
|
65
|
+
sql ||= (@@OP_2_SQL[expr] || @@empty_hash)[type] if expr
|
66
|
+
sql ||= (@@OP_2_SQL[true])[type]
|
67
|
+
raise Error, "Unknown operator #{type.inspect}" unless sql
|
68
|
+
|
69
|
+
#if @verbose
|
70
|
+
# $stderr.puts "#{sql.inspect} "
|
71
|
+
#end
|
72
|
+
|
73
|
+
sql
|
74
|
+
end
|
75
|
+
|
76
|
+
def emit_sql(expr)
|
77
|
+
case type = expr && expr[0]
|
78
|
+
when :not
|
79
|
+
emit("#{op_to_sql(type)} (")
|
80
|
+
emit_sql(expr[1])
|
81
|
+
emit(")")
|
82
|
+
|
83
|
+
when :and, :or
|
84
|
+
emit("(")
|
85
|
+
emit_sql(expr[1])
|
86
|
+
emit(" #{op_to_sql(type)} ")
|
87
|
+
emit_sql(expr[2])
|
88
|
+
emit(")")
|
89
|
+
|
90
|
+
when :lt, :gt, :le, :ge, :eq, :ne
|
91
|
+
emit("(")
|
92
|
+
emit(target)
|
93
|
+
emit(" #{op_to_sql(type, expr[1])} ")
|
94
|
+
emit_sql_value(expr[1])
|
95
|
+
emit(")")
|
96
|
+
|
97
|
+
when :between
|
98
|
+
emit("((")
|
99
|
+
emit(target)
|
100
|
+
emit(" >= ")
|
101
|
+
emit_sql_value(expr[1])
|
102
|
+
emit(") #{op_to_sql(:and)} (")
|
103
|
+
emit(target)
|
104
|
+
emit(" <= ")
|
105
|
+
emit_sql_value(expr[2])
|
106
|
+
emit("))")
|
107
|
+
|
108
|
+
when :range
|
109
|
+
emit("((")
|
110
|
+
emit(target)
|
111
|
+
emit(" >= ")
|
112
|
+
emit_sql_value(expr[1])
|
113
|
+
emit(") #{op_to_sql(:and)} (")
|
114
|
+
emit(target)
|
115
|
+
emit(" < ")
|
116
|
+
emit_sql_value(expr[2])
|
117
|
+
emit("))")
|
118
|
+
|
119
|
+
when :like
|
120
|
+
emit("(")
|
121
|
+
emit(target)
|
122
|
+
emit(" LIKE ")
|
123
|
+
expr_1 = expr[1]
|
124
|
+
expr_1 = [ :string, expr_1[1], expr_1[1] ]
|
125
|
+
expr_1[1] = expr_1[1].gsub(/[%_\\]/){|x| "\\#{x}"} # Escape '%' and '_' in query
|
126
|
+
expr_1[1] = '%' + expr_1[1] + '%'
|
127
|
+
# $stderr.puts "LIKE #{expr_1[1]}"
|
128
|
+
expr_1[2] = expr_1[1]
|
129
|
+
emit_sql_value(expr_1, :no_internal_escape)
|
130
|
+
emit(")")
|
131
|
+
|
132
|
+
else
|
133
|
+
emit("(")
|
134
|
+
emit(target)
|
135
|
+
emit(" #{op_to_sql(:eq, expr)} ")
|
136
|
+
emit_sql_value(expr)
|
137
|
+
emit(")")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_string(value)
|
142
|
+
to_simple(value)[0].to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
def emit_sql_value(value, *options)
|
146
|
+
value, type = to_simple(value)
|
147
|
+
|
148
|
+
if values_inline
|
149
|
+
case type
|
150
|
+
when :null
|
151
|
+
value = sql_quote(value)
|
152
|
+
when :number, :boolean
|
153
|
+
value = value.to_s
|
154
|
+
else
|
155
|
+
value = sql_quote(value, *options)
|
156
|
+
end
|
157
|
+
|
158
|
+
emit(value)
|
159
|
+
else
|
160
|
+
emit('?')
|
161
|
+
@values << value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_simple(value)
|
166
|
+
case type = value && value[0]
|
167
|
+
when :null
|
168
|
+
value = nil
|
169
|
+
|
170
|
+
when :string, :word, :number
|
171
|
+
value = value[2]
|
172
|
+
|
173
|
+
when :boolean
|
174
|
+
value = value[2] ? 1 : 0 # MySQL tinyint(1)
|
175
|
+
|
176
|
+
when :money
|
177
|
+
type = :number
|
178
|
+
value = value[2].rep
|
179
|
+
|
180
|
+
# MySQL-specific timedate formats!?!
|
181
|
+
when :year
|
182
|
+
value = "#{'%04d' % value[2]}-01-01 00:00:00"
|
183
|
+
|
184
|
+
when :month
|
185
|
+
value = "#{'%04d' % value[2]}-#{'%02d' % value[3]}-01 00:00:00"
|
186
|
+
|
187
|
+
when :day
|
188
|
+
value = "#{'%04d' % value[2]}-#{'%02d' % value[3]}-#{'%02d' % value[4]} 00:00:00"
|
189
|
+
|
190
|
+
when :hour
|
191
|
+
value = "#{'%04d' % value[2]}-#{'%02d' % value[3]}-#{'%02d' % value[4]} #{'%02d' % value[5]}:00:00"
|
192
|
+
|
193
|
+
when :minute
|
194
|
+
value = "#{'%04d' % value[2]}-#{'%02d' % value[3]}-#{'%02d' % value[4]} #{'%02d' % value[5]}:#{'%02d' % value[6]}:00"
|
195
|
+
when :second
|
196
|
+
value = "#{'%04d' % value[2]}-#{'%02d' % value[3]}-#{'%02d' % value[4]} #{'%02d' % value[5]}:#{'%02d' % value[6]}:#{'%02d' % value[7]}"
|
197
|
+
|
198
|
+
else
|
199
|
+
raise Error, "Unknown value type #{value.inspect}"
|
200
|
+
end
|
201
|
+
|
202
|
+
[ value, type ]
|
203
|
+
end
|
204
|
+
|
205
|
+
def sql_quote(value, no_internal_escape = false)
|
206
|
+
return 'NULL' if value.nil?
|
207
|
+
"'" + (no_internal_escape ?
|
208
|
+
value.to_s.gsub(/(['])/){|x| "\\#{x}"} :
|
209
|
+
value.to_s.gsub(/(['\\])/){|x| "\\#{x}"}) +
|
210
|
+
"'"
|
211
|
+
end
|
212
|
+
|
213
|
+
def emit(raw)
|
214
|
+
@expr << raw
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end # module
|