rgviz-rails 0.43 → 0.44
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/README.rdoc +101 -0
- data/lib/rgviz_rails.rb +1 -0
- data/lib/rgviz_rails/executor.rb +10 -26
- data/{rails → lib/rgviz_rails}/init.rb +31 -19
- data/lib/rgviz_rails/view_helper.rb +86 -82
- data/spec/blueprints.rb +0 -7
- data/spec/rgviz/executor_spec.rb +2 -11
- data/spec/spec_helper.rb +0 -10
- metadata +20 -12
- data/README +0 -243
- data/spec/models/pet.rb +0 -3
data/README.rdoc
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
== Rgviz-rails
|
2
|
+
|
3
|
+
This library makes it easy to implement a Visualization data source so that you can easily chart or visualize your data from ActiveRecord[http://ar.rubyonrails.org/] models. The library implements the {Google Visualization API wire protocol}[http://code.google.com/apis/visualization/documentation/dev/implementing_data_source.html].
|
4
|
+
|
5
|
+
It also allows you to {render the visualizations in a view template}[https://github.com/asterite/rgviz-rails/wiki/Showing-a-visualization-in-a-view] in a very simple but powerful way.
|
6
|
+
|
7
|
+
This library is built on top of rgviz[https://github.com/asterite/rgviz].
|
8
|
+
|
9
|
+
=== Installation
|
10
|
+
|
11
|
+
gem install rgviz-rails
|
12
|
+
|
13
|
+
In your environment.rb
|
14
|
+
|
15
|
+
config.gem "rgviz-rails", :lib => 'rgviz_rails'
|
16
|
+
|
17
|
+
=== Usage
|
18
|
+
|
19
|
+
To make a method in your controller be a visualization API endpoint:
|
20
|
+
|
21
|
+
class VizController < ApplicationController
|
22
|
+
|
23
|
+
def person
|
24
|
+
# Person is an ActiveRecord::Base class
|
25
|
+
render :rgviz => Person
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
So for example if +Person+ has +name+ and +age+, pointing your browser to:
|
31
|
+
|
32
|
+
http://localhost:3000/viz/person?select name where age > 20 limit 5
|
33
|
+
|
34
|
+
would render the necessary javascript code that implements the Google Visualization API wire protocol.
|
35
|
+
|
36
|
+
=== Extensions
|
37
|
+
|
38
|
+
To enable the extensions defined by rgviz you need to specify it in the render method:
|
39
|
+
|
40
|
+
render :rgviz => Person, :extensions => true
|
41
|
+
|
42
|
+
=== Associations
|
43
|
+
|
44
|
+
If you want to filter, order by or group by columns that are in a model's association you can use underscores. This is better understood with an example:
|
45
|
+
|
46
|
+
class Person < ActiveRecord::Base
|
47
|
+
belongs_to :city
|
48
|
+
end
|
49
|
+
|
50
|
+
class City < ActiveRecord::Base
|
51
|
+
belongs_to :country
|
52
|
+
end
|
53
|
+
|
54
|
+
class Country < ActiveRecord::Base
|
55
|
+
end
|
56
|
+
|
57
|
+
To select the name of the city each person belongs to:
|
58
|
+
|
59
|
+
select city_name
|
60
|
+
|
61
|
+
To select the name of the country of the city each person belongs to:
|
62
|
+
|
63
|
+
select city_country_name
|
64
|
+
|
65
|
+
A slightly more complex example:
|
66
|
+
|
67
|
+
select avg(age) where city_country_name = 'Argentina' group by city_name
|
68
|
+
|
69
|
+
The library will make it in just one query, writing all the SQL joins for you.
|
70
|
+
|
71
|
+
=== Extra conditions
|
72
|
+
|
73
|
+
Sometimes you want to limit your results the query will work with. You can do it like this:
|
74
|
+
|
75
|
+
render :rgviz => Person, :conditions => ['age > ?', 20]
|
76
|
+
|
77
|
+
Or also:
|
78
|
+
|
79
|
+
render :rgviz => Person, :conditions => 'age > 20'
|
80
|
+
|
81
|
+
=== Preprocessing
|
82
|
+
|
83
|
+
If you need to tweak a result before returning it, just include a block:
|
84
|
+
|
85
|
+
render :rgviz => Person do |table|
|
86
|
+
# Modify the Rgviz::Table object
|
87
|
+
end
|
88
|
+
|
89
|
+
=== Showing a visualization in a view
|
90
|
+
|
91
|
+
You can invoke the rgviz method in your views. {Read more about this}[https://github.com/asterite/rgviz-rails/wiki/Showing-a-visualization-in-a-view].
|
92
|
+
|
93
|
+
You can always do it the {old way}[http://code.google.com/apis/visualization/documentation/using_overview.html]. {Read more about this}
|
94
|
+
|
95
|
+
=== Current Limitations
|
96
|
+
* The *format* clause is ignored (If someone knows of a working icu library for ruby, please tell me)
|
97
|
+
* Only supports MySQL, PostgreSQL and SQLite adapters
|
98
|
+
* These scalar functions are not supported for SQLite: *millisecond*, *quarter*
|
99
|
+
* These scalar functions are not supported for MySQL: *millisecond*
|
100
|
+
* The function *toDate* doesn't accept a number as its argument
|
101
|
+
* The *tsv* output format is not supported
|
data/lib/rgviz_rails.rb
CHANGED
data/lib/rgviz_rails/executor.rb
CHANGED
@@ -517,32 +517,16 @@ module Rgviz
|
|
517
517
|
col = klass.send(:columns).select{|x| x.name == name}.first
|
518
518
|
return [klass, col, joins] if col
|
519
519
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
return nil
|
531
|
-
end
|
532
|
-
|
533
|
-
before = name[0 ... idx]
|
534
|
-
new_name = name[idx + 1 .. -1]
|
535
|
-
|
536
|
-
assoc = klass.send :reflect_on_association, before.to_sym
|
537
|
-
if assoc
|
538
|
-
name = new_name
|
539
|
-
klass = assoc.klass
|
540
|
-
joins << assoc
|
541
|
-
break
|
542
|
-
end
|
543
|
-
|
544
|
-
offset = idx + 1
|
545
|
-
end
|
520
|
+
idx = name.index '_'
|
521
|
+
return nil if not idx
|
522
|
+
|
523
|
+
before = name[0 ... idx]
|
524
|
+
name = name[idx + 1 .. -1]
|
525
|
+
|
526
|
+
assoc = klass.send :reflect_on_association, before.to_sym
|
527
|
+
raise "Unknown association #{before}" unless assoc
|
528
|
+
klass = assoc.klass
|
529
|
+
joins << assoc
|
546
530
|
end
|
547
531
|
end
|
548
532
|
|
@@ -2,63 +2,75 @@ require "rgviz_rails/view_helper"
|
|
2
2
|
# includes the view helper to ActionView::Base
|
3
3
|
ActionView::Base.send(:include, Rgviz::ViewHelper)
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def render(*args, &block)
|
5
|
+
def _define_rgviz_class
|
6
|
+
::ActionController::Base.module_eval do
|
7
|
+
def render_with_rgviz(*args, &block)
|
9
8
|
if args.length == 1 && args[0].kind_of?(Hash)
|
10
|
-
hash = args.first
|
9
|
+
hash = args.first
|
11
10
|
case hash[:rgviz]
|
12
|
-
when nil then
|
11
|
+
when nil then render_without_rgviz *args, &block
|
13
12
|
else
|
14
13
|
model = hash[:rgviz]
|
15
14
|
conditions = hash[:conditions]
|
16
15
|
extensions = hash[:extensions]
|
17
16
|
query = params[:tq]
|
18
17
|
tqx = params[:tqx] || ''
|
19
|
-
|
18
|
+
|
20
19
|
tqx = Rgviz::Tqx.parse(tqx)
|
21
|
-
|
20
|
+
|
22
21
|
begin
|
23
22
|
executor = Rgviz::Executor.new model, query
|
24
23
|
options = {}
|
25
24
|
options[:conditions] = conditions if conditions
|
26
25
|
options[:extensions] = extensions if extensions
|
27
26
|
table = executor.execute options
|
28
|
-
|
27
|
+
|
29
28
|
yield table if block_given?
|
30
|
-
|
29
|
+
|
31
30
|
case tqx['out']
|
32
31
|
when 'json'
|
33
|
-
|
32
|
+
render_without_rgviz :text => Rgviz::JsRenderer.render(table, tqx)
|
34
33
|
when 'html'
|
35
|
-
|
34
|
+
render_without_rgviz :text => Rgviz::HtmlRenderer.render(table)
|
36
35
|
when 'csv'
|
37
36
|
csv_output = Rgviz::CsvRenderer.render(table)
|
38
37
|
if tqx['outFileName']
|
39
38
|
send_data csv_output, :filename => tqx['outFileName'], :type => 'text/csv'
|
40
39
|
else
|
41
|
-
|
40
|
+
render_without_rgviz :text => csv_output
|
42
41
|
end
|
43
42
|
else
|
44
|
-
|
43
|
+
render_without_rgviz :text => Rgviz::JsRenderer.render_error('not_supported', "Unsupported output type: #{out}", tqx)
|
45
44
|
end
|
46
45
|
rescue ParseException => e
|
47
46
|
case tqx['out']
|
48
47
|
when 'json'
|
49
|
-
|
48
|
+
render_without_rgviz :text => Rgviz::JsRenderer.render_error('invalid_query', e.message, tqx)
|
50
49
|
when 'html'
|
51
|
-
|
50
|
+
render_without_rgviz :text => "<b>Error:</b> #{e.message}"
|
52
51
|
when 'csv'
|
53
|
-
|
52
|
+
render_without_rgviz :text => "Error: #{e.message}"
|
54
53
|
else
|
55
|
-
|
54
|
+
render_without_rgviz :text => "<b>Unsupported output type:</b> #{out}"
|
56
55
|
end
|
57
56
|
end
|
58
57
|
end
|
59
58
|
else
|
60
|
-
|
59
|
+
render_without_rgviz *args, &block
|
61
60
|
end
|
62
61
|
end
|
62
|
+
alias_method_chain :render, :rgviz
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if Rails::VERSION::MAJOR == 2
|
67
|
+
config.after_initialize do
|
68
|
+
_define_rgviz_class
|
69
|
+
end
|
70
|
+
else
|
71
|
+
class Railtie < Rails::Railtie
|
72
|
+
initializer "define rgviz class" do
|
73
|
+
_define_rgviz_class
|
74
|
+
end
|
63
75
|
end
|
64
76
|
end
|
@@ -6,7 +6,7 @@ module Rgviz
|
|
6
6
|
i = 0
|
7
7
|
opts.each do |key, value|
|
8
8
|
key = key.to_s.gsub('"', '\"')
|
9
|
-
|
9
|
+
|
10
10
|
@s << ',' if i > 0
|
11
11
|
@s << "\"#{key}\":"
|
12
12
|
if special_keys.include?(key) || !value.kind_of?(String)
|
@@ -19,9 +19,9 @@ module Rgviz
|
|
19
19
|
end
|
20
20
|
@s << '}'
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
options = options.with_indifferent_access
|
24
|
-
|
24
|
+
|
25
25
|
id = options[:id]
|
26
26
|
kind = options[:kind]
|
27
27
|
url = options[:url]
|
@@ -30,74 +30,74 @@ module Rgviz
|
|
30
30
|
html = options[:html] || {}
|
31
31
|
hidden = options[:hidden]
|
32
32
|
extensions = options[:extensions]
|
33
|
-
|
33
|
+
|
34
34
|
rgviz_events, google_events = events.partition{|x| x.to_s.start_with? 'rgviz'}
|
35
35
|
rgviz_events = rgviz_events.inject(Hash.new){|h, y| h[y[0]] = y[1]; h}
|
36
36
|
rgviz_events = rgviz_events.with_indifferent_access
|
37
|
-
|
37
|
+
|
38
38
|
html_prefix = (options[:html_prefix] || 'html').to_s
|
39
39
|
js_prefix = (options[:js_prefix] || 'js').to_s
|
40
40
|
param_prefix = (options[:param_prefix] || 'param').to_s
|
41
|
-
|
41
|
+
|
42
42
|
html_prefix += '_'
|
43
43
|
js_prefix += '_'
|
44
44
|
param_prefix += '_'
|
45
|
-
|
45
|
+
|
46
46
|
debug = options[:debug]
|
47
47
|
opts = options[:options] || {}
|
48
48
|
opts[:width] = 640 unless opts[:width]
|
49
49
|
opts[:height] = 480 unless opts[:height]
|
50
|
-
|
50
|
+
|
51
51
|
params = []
|
52
52
|
uses_rgviz_get_value = false
|
53
53
|
uses_rgviz_append = false
|
54
|
-
|
54
|
+
|
55
55
|
visitor = MagicNamesVisitor.new(html_prefix, js_prefix, param_prefix)
|
56
|
-
|
56
|
+
|
57
57
|
special_keys = []
|
58
|
-
|
58
|
+
|
59
59
|
opts.each do |key, value|
|
60
60
|
next unless value.kind_of?(String)
|
61
|
-
|
61
|
+
|
62
62
|
source = visitor.get_source(value, false)
|
63
63
|
next unless source[:source]
|
64
|
-
|
64
|
+
|
65
65
|
special_keys << key
|
66
|
-
|
66
|
+
|
67
67
|
case source[:source]
|
68
68
|
when :html
|
69
|
-
opts[key] = "rgviz_get_value('#{source[:id]}')"
|
70
|
-
uses_rgviz_get_value = true
|
69
|
+
opts[key] = "rgviz_get_value('#{source[:id]}')"
|
70
|
+
uses_rgviz_get_value = true
|
71
71
|
when :js
|
72
72
|
opts[key] = "#{source[:id]}()"
|
73
73
|
when :param
|
74
74
|
opts[key] = "param_#{source[:id]}"
|
75
75
|
params << source[:id].to_i unless params.include?(source[:id])
|
76
|
-
end
|
76
|
+
end
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
opts = opts_to_json(opts, special_keys)
|
80
|
-
|
80
|
+
|
81
81
|
raise "Must specify an :id" unless id
|
82
82
|
raise "Must specify a :kind" unless kind
|
83
83
|
raise "Must specify a :url" unless url
|
84
|
-
|
84
|
+
|
85
85
|
url = url_for url
|
86
|
-
|
86
|
+
|
87
87
|
# Parse the query
|
88
88
|
query = Parser.parse query, :extensions => extensions
|
89
|
-
|
89
|
+
|
90
90
|
# And replace the html_ and javascript_ magic names
|
91
91
|
query.accept visitor
|
92
92
|
query_builder = visitor.query_builder
|
93
93
|
query_builder_var = visitor.query_builder_var
|
94
|
-
|
94
|
+
|
95
95
|
uses_rgviz_get_value |= visitor.uses_rgviz_get_value?
|
96
96
|
uses_rgviz_append |= visitor.uses_rgviz_append?
|
97
|
-
|
97
|
+
|
98
98
|
visitor.params.each{|p| params << p unless params.include?(p)}
|
99
99
|
params = params.sort!.map{|i| "param_#{i}"}
|
100
|
-
|
100
|
+
|
101
101
|
out = ''
|
102
102
|
|
103
103
|
# Output the google jsapi tag the first time
|
@@ -107,15 +107,15 @@ module Rgviz
|
|
107
107
|
end
|
108
108
|
# Now the real script
|
109
109
|
out << "<script type=\"text/javascript\">\n"
|
110
|
-
|
110
|
+
|
111
111
|
# Define a function to get the value of an html element
|
112
|
-
if uses_rgviz_get_value && !@defined_rgviz_get_value
|
112
|
+
if uses_rgviz_get_value && !@defined_rgviz_get_value
|
113
113
|
out << "function rgviz_get_value(id) {\n"
|
114
114
|
out << "var e = document.getElementById(id);\n"
|
115
115
|
out << "var n = e.tagName.toLowerCase();\n"
|
116
116
|
out << "var s = null;\n"
|
117
117
|
out << "if (n == 'select' && e.multiple) {\n"
|
118
|
-
out << "var s = [];\n"
|
118
|
+
out << "var s = [];\n"
|
119
119
|
out << "var o = e.options;\n"
|
120
120
|
out << "for(var i = 0; i < o.length; i++)\n"
|
121
121
|
out << "if (o[i].selected) s.push(o[i].value);\n"
|
@@ -128,7 +128,7 @@ module Rgviz
|
|
128
128
|
out << "}\n"
|
129
129
|
@defined_rgviz_get_value = true
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
# Define a function to append the value of a magic something
|
133
133
|
if uses_rgviz_append && !@defined_rgviz_append
|
134
134
|
out << "function rgviz_append(s, b, a) {\n"
|
@@ -147,27 +147,27 @@ module Rgviz
|
|
147
147
|
out << "}\n"
|
148
148
|
@defined_rgviz_append = true
|
149
149
|
end
|
150
|
-
|
150
|
+
|
151
151
|
# Load visualizations and the package, if not already loaded
|
152
|
-
pack = kind.downcase
|
152
|
+
pack = kind.downcase
|
153
153
|
@packages ||= []
|
154
154
|
unless @packages.include?(pack)
|
155
155
|
out << "google.load(\"visualization\", \"1\", {'packages':['#{pack}']});\n"
|
156
156
|
@packages << pack
|
157
157
|
end
|
158
|
-
|
158
|
+
|
159
159
|
callback = "rgviz_draw_#{id}"
|
160
|
-
|
160
|
+
|
161
161
|
# Set the callback if the function doesn't have params and if the
|
162
162
|
# user didn't request to hide the visualization
|
163
163
|
if !hidden && params.empty?
|
164
164
|
out << "google.setOnLoadCallback(#{callback});\n"
|
165
165
|
end
|
166
|
-
|
166
|
+
|
167
167
|
# Define the visualization var and data
|
168
168
|
out << "var rgviz_#{id} = null;\n"
|
169
169
|
out << "var rgviz_#{id}_data = null;\n"
|
170
|
-
|
170
|
+
|
171
171
|
# And define the callback
|
172
172
|
out << "function #{callback}(#{params.join(', ')}) {\n"
|
173
173
|
out << "#{rgviz_events[:rgviz_start]}('#{id}');\n" if rgviz_events[:rgviz_start]
|
@@ -185,24 +185,28 @@ module Rgviz
|
|
185
185
|
out << "#{rgviz_events[:rgviz_end]}('#{id}');\n" if rgviz_events[:rgviz_end]
|
186
186
|
out << "});\n"
|
187
187
|
out << "}\n"
|
188
|
-
|
188
|
+
|
189
189
|
out << "</script>\n"
|
190
|
-
|
190
|
+
|
191
191
|
# Write the div
|
192
192
|
out << "<div id=\"#{id}\""
|
193
193
|
html.each do |key, value|
|
194
194
|
out << " #{key}=\"#{h value}\""
|
195
195
|
end
|
196
196
|
out << "></div>\n"
|
197
|
-
|
197
|
+
|
198
198
|
@first_time = 0
|
199
|
-
|
200
|
-
|
199
|
+
|
200
|
+
if defined? :raw
|
201
|
+
raw out
|
202
|
+
else
|
203
|
+
out
|
204
|
+
end
|
201
205
|
end
|
202
|
-
|
206
|
+
|
203
207
|
module_function :rgviz
|
204
208
|
end
|
205
|
-
|
209
|
+
|
206
210
|
class MagicNamesVisitor < Visitor
|
207
211
|
def initialize(html_prefix, js_prefix, param_prefix)
|
208
212
|
@html_prefix = html_prefix
|
@@ -211,27 +215,27 @@ module Rgviz
|
|
211
215
|
@s = ''
|
212
216
|
@params = []
|
213
217
|
end
|
214
|
-
|
218
|
+
|
215
219
|
def query_builder
|
216
220
|
@s.strip
|
217
221
|
end
|
218
|
-
|
222
|
+
|
219
223
|
def query_builder_var
|
220
224
|
'q'
|
221
225
|
end
|
222
|
-
|
226
|
+
|
223
227
|
def params
|
224
228
|
@params
|
225
229
|
end
|
226
|
-
|
230
|
+
|
227
231
|
def uses_rgviz_get_value?
|
228
232
|
@uses_rgviz_get_value
|
229
233
|
end
|
230
|
-
|
234
|
+
|
231
235
|
def uses_rgviz_append?
|
232
236
|
@uses_rgviz_append
|
233
237
|
end
|
234
|
-
|
238
|
+
|
235
239
|
def visit_query(node)
|
236
240
|
@s << "var q = '"
|
237
241
|
node.select.accept self if node.select
|
@@ -241,20 +245,20 @@ module Rgviz
|
|
241
245
|
node.order_by.accept self if node.order_by
|
242
246
|
@s << "limit #{node.limit} " if node.limit
|
243
247
|
@s << "offset #{node.offset} " if node.offset
|
244
|
-
if node.labels
|
248
|
+
if node.labels
|
245
249
|
@s << "label "
|
246
250
|
node.labels.each_with_index do |l, i|
|
247
|
-
@s << ', ' if i > 0
|
251
|
+
@s << ', ' if i > 0
|
248
252
|
l.accept self
|
249
253
|
end
|
250
|
-
end
|
254
|
+
end
|
251
255
|
if node.formats
|
252
256
|
@s << "format "
|
253
257
|
node.formats.each_with_index do |f, i|
|
254
|
-
@s << ', ' if i > 0
|
258
|
+
@s << ', ' if i > 0
|
255
259
|
f.accept self
|
256
260
|
end
|
257
|
-
end
|
261
|
+
end
|
258
262
|
if node.options
|
259
263
|
@s << "options "
|
260
264
|
@s << "no_values " if node.options.no_values
|
@@ -263,35 +267,35 @@ module Rgviz
|
|
263
267
|
@s << "';\n"
|
264
268
|
false
|
265
269
|
end
|
266
|
-
|
270
|
+
|
267
271
|
def visit_select(node)
|
268
272
|
@s << "select ";
|
269
273
|
print_columns node
|
270
274
|
@s << " "
|
271
275
|
false
|
272
276
|
end
|
273
|
-
|
277
|
+
|
274
278
|
def visit_where(node)
|
275
279
|
@s << "where "
|
276
280
|
node.expression.accept self
|
277
281
|
@s << " "
|
278
282
|
false
|
279
283
|
end
|
280
|
-
|
284
|
+
|
281
285
|
def visit_group_by(node)
|
282
286
|
@s << "group by "
|
283
287
|
print_columns node
|
284
288
|
@s << " "
|
285
289
|
false
|
286
290
|
end
|
287
|
-
|
291
|
+
|
288
292
|
def visit_pivot(node)
|
289
293
|
@s << "pivot "
|
290
294
|
print_columns node
|
291
295
|
@s << " "
|
292
296
|
false
|
293
297
|
end
|
294
|
-
|
298
|
+
|
295
299
|
def visit_order_by(node)
|
296
300
|
@s << "order by "
|
297
301
|
node.sorts.each_with_index do |s, i|
|
@@ -303,7 +307,7 @@ module Rgviz
|
|
303
307
|
@s << " "
|
304
308
|
false
|
305
309
|
end
|
306
|
-
|
310
|
+
|
307
311
|
def visit_label(node)
|
308
312
|
node.column.accept self
|
309
313
|
@s << ' '
|
@@ -315,7 +319,7 @@ module Rgviz
|
|
315
319
|
end
|
316
320
|
false
|
317
321
|
end
|
318
|
-
|
322
|
+
|
319
323
|
def visit_format(node)
|
320
324
|
node.column.accept self
|
321
325
|
@s << ' '
|
@@ -326,7 +330,7 @@ module Rgviz
|
|
326
330
|
end
|
327
331
|
false
|
328
332
|
end
|
329
|
-
|
333
|
+
|
330
334
|
def visit_logical_expression(node)
|
331
335
|
@s += "("
|
332
336
|
node.operands.each_with_index do |operand, i|
|
@@ -336,9 +340,9 @@ module Rgviz
|
|
336
340
|
@s += ")"
|
337
341
|
false
|
338
342
|
end
|
339
|
-
|
343
|
+
|
340
344
|
def visit_binary_expression(node)
|
341
|
-
if node.operator == BinaryExpression::Eq
|
345
|
+
if node.operator == BinaryExpression::Eq
|
342
346
|
source = has_magic_name?(node.right)
|
343
347
|
if source
|
344
348
|
@s << "';\n"
|
@@ -346,7 +350,7 @@ module Rgviz
|
|
346
350
|
when :html
|
347
351
|
@s << "var s = rgviz_get_value('#{source[:id]}');\n"
|
348
352
|
append_selections node, source
|
349
|
-
|
353
|
+
|
350
354
|
@uses_rgviz_get_value = true
|
351
355
|
when :js
|
352
356
|
@s << "var s = #{source[:id]}();\n"
|
@@ -367,7 +371,7 @@ module Rgviz
|
|
367
371
|
node.right.accept self
|
368
372
|
false
|
369
373
|
end
|
370
|
-
|
374
|
+
|
371
375
|
def visit_unary_expression(node)
|
372
376
|
if node.operator == UnaryExpression::Not
|
373
377
|
@s << "not "
|
@@ -388,7 +392,7 @@ module Rgviz
|
|
388
392
|
append_before_source_type source[:type]
|
389
393
|
@s << " + rgviz_get_value('#{source[:id]}') + "
|
390
394
|
append_after_source_type source[:type]
|
391
|
-
|
395
|
+
|
392
396
|
@uses_rgviz_get_value = true
|
393
397
|
when :js
|
394
398
|
append_before_source_type source[:type]
|
@@ -401,35 +405,35 @@ module Rgviz
|
|
401
405
|
@params << source[:id].to_i unless @params.include?(source[:id])
|
402
406
|
end
|
403
407
|
end
|
404
|
-
|
408
|
+
|
405
409
|
def visit_number_column(node)
|
406
410
|
@s << node.value.to_s
|
407
411
|
end
|
408
|
-
|
412
|
+
|
409
413
|
def visit_string_column(node)
|
410
414
|
value = node.value.gsub('"', '\"')
|
411
415
|
@s << "\"#{value}\""
|
412
416
|
end
|
413
|
-
|
417
|
+
|
414
418
|
def visit_boolean_column(node)
|
415
419
|
@s << node.value.to_s
|
416
420
|
end
|
417
|
-
|
421
|
+
|
418
422
|
def visit_date_column(node)
|
419
423
|
@s << "date \"#{node.value.to_s}\""
|
420
424
|
end
|
421
|
-
|
425
|
+
|
422
426
|
def visit_date_time_column(node)
|
423
427
|
@s << "date \"#{node.value.strftime('%Y-%m-%d %H:%M:%S')}\""
|
424
428
|
end
|
425
|
-
|
429
|
+
|
426
430
|
def visit_time_of_day_column(node)
|
427
431
|
@s << "date \"#{node.value.strftime('%H:%M:%S')}\""
|
428
432
|
end
|
429
|
-
|
433
|
+
|
430
434
|
def visit_scalar_function_column(node)
|
431
435
|
case node.function
|
432
|
-
when ScalarFunctionColumn::Sum, ScalarFunctionColumn::Difference,
|
436
|
+
when ScalarFunctionColumn::Sum, ScalarFunctionColumn::Difference,
|
433
437
|
ScalarFunctionColumn::Product, ScalarFunctionColumn::Quotient
|
434
438
|
node.arguments[0].accept node
|
435
439
|
@s << " #{node.function} "
|
@@ -444,21 +448,21 @@ module Rgviz
|
|
444
448
|
end
|
445
449
|
false
|
446
450
|
end
|
447
|
-
|
451
|
+
|
448
452
|
def visit_aggregate_column(node)
|
449
453
|
@s << "#{node.function}("
|
450
454
|
node.argument.accept self
|
451
455
|
@s << ")"
|
452
456
|
false
|
453
457
|
end
|
454
|
-
|
458
|
+
|
455
459
|
def print_columns(node)
|
456
460
|
node.columns.each_with_index do |c, i|
|
457
461
|
@s << ', ' if i > 0
|
458
462
|
c.accept self
|
459
463
|
end
|
460
464
|
end
|
461
|
-
|
465
|
+
|
462
466
|
def get_source(name, include_type = true)
|
463
467
|
if name.start_with?(@html_prefix)
|
464
468
|
if include_type
|
@@ -482,7 +486,7 @@ module Rgviz
|
|
482
486
|
{}
|
483
487
|
end
|
484
488
|
end
|
485
|
-
|
489
|
+
|
486
490
|
def get_source_type(source, name)
|
487
491
|
if name.start_with?('number_')
|
488
492
|
{:source => source, :id => name[7 .. -1], :type => :number}
|
@@ -498,7 +502,7 @@ module Rgviz
|
|
498
502
|
{:source => source, :id => name, :type => :string}
|
499
503
|
end
|
500
504
|
end
|
501
|
-
|
505
|
+
|
502
506
|
def append_before_source_type(type)
|
503
507
|
case type
|
504
508
|
when :number
|
@@ -513,7 +517,7 @@ module Rgviz
|
|
513
517
|
@s << "timeofday \"'"
|
514
518
|
end
|
515
519
|
end
|
516
|
-
|
520
|
+
|
517
521
|
def append_after_source_type(type)
|
518
522
|
case type
|
519
523
|
when :number
|
@@ -522,7 +526,7 @@ module Rgviz
|
|
522
526
|
@s << "'\""
|
523
527
|
end
|
524
528
|
end
|
525
|
-
|
529
|
+
|
526
530
|
def append_selections(node, source)
|
527
531
|
@s << "q += rgviz_append(s, '";
|
528
532
|
node.left.accept self
|
@@ -533,7 +537,7 @@ module Rgviz
|
|
533
537
|
@s << "');\n"
|
534
538
|
@uses_rgviz_append = true
|
535
539
|
end
|
536
|
-
|
540
|
+
|
537
541
|
def has_magic_name?(node)
|
538
542
|
return false unless node.kind_of?(IdColumn)
|
539
543
|
source = get_source node.name
|
data/spec/blueprints.rb
CHANGED
data/spec/rgviz/executor_spec.rb
CHANGED
@@ -5,12 +5,11 @@ include Rgviz
|
|
5
5
|
|
6
6
|
describe Executor do
|
7
7
|
before :each do
|
8
|
-
[Person,
|
8
|
+
[Person, City, Country].each &:delete_all
|
9
9
|
end
|
10
10
|
|
11
11
|
def exec(query, options = {})
|
12
|
-
|
13
|
-
exec = Executor.new clazz, query
|
12
|
+
exec = Executor.new Person, query
|
14
13
|
exec.execute options
|
15
14
|
end
|
16
15
|
|
@@ -287,14 +286,6 @@ describe Executor do
|
|
287
286
|
|
288
287
|
it_processes_single_select_column "1 options no_values", 'c0', :number, nil, "1"
|
289
288
|
|
290
|
-
it_processes_single_select_column "1 where city is null", 'c0', :number, 1, "1" do
|
291
|
-
Person.make :city => nil
|
292
|
-
end
|
293
|
-
|
294
|
-
it_processes_single_select_column "the_city_name where the_city_name = 'foo'", 'the_city_name', :string, 'foo', "the_city_name", :class => Pet do
|
295
|
-
Pet.make :the_city => (City.make :name => 'foo')
|
296
|
-
end
|
297
|
-
|
298
289
|
it "processes pivot" do
|
299
290
|
Person.make :name => 'Eng', :birthday => '2000-01-12', :age => 1000
|
300
291
|
Person.make :name => 'Eng', :birthday => '2000-01-12', :age => 500
|
data/spec/spec_helper.rb
CHANGED
@@ -32,19 +32,9 @@ ActiveRecord::Schema.define do
|
|
32
32
|
t.datetime "updated_at"
|
33
33
|
t.integer "city_id"
|
34
34
|
end
|
35
|
-
|
36
|
-
create_table "pets", :force => true do |t|
|
37
|
-
t.string "name"
|
38
|
-
t.integer "age"
|
39
|
-
t.date "birthday"
|
40
|
-
t.datetime "created_at"
|
41
|
-
t.datetime "updated_at"
|
42
|
-
t.integer "city_id"
|
43
|
-
end
|
44
35
|
end
|
45
36
|
|
46
37
|
require File.dirname(__FILE__) + '/models/person'
|
47
|
-
require File.dirname(__FILE__) + '/models/pet'
|
48
38
|
require File.dirname(__FILE__) + '/models/city'
|
49
39
|
require File.dirname(__FILE__) + '/models/country'
|
50
40
|
|
metadata
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rgviz-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 93
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
7
|
+
- 44
|
8
|
+
version: "0.44"
|
10
9
|
platform: ruby
|
11
10
|
authors:
|
12
11
|
- Ary Borenszweig
|
@@ -14,10 +13,22 @@ autorequire:
|
|
14
13
|
bindir: bin
|
15
14
|
cert_chain: []
|
16
15
|
|
17
|
-
date: 2010-
|
16
|
+
date: 2010-12-11 00:00:00 -03:00
|
18
17
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: rgviz
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
21
32
|
description:
|
22
33
|
email: aborenszweig@manas.com.ar
|
23
34
|
executables: []
|
@@ -25,7 +36,7 @@ executables: []
|
|
25
36
|
extensions: []
|
26
37
|
|
27
38
|
extra_rdoc_files:
|
28
|
-
- README
|
39
|
+
- README.rdoc
|
29
40
|
files:
|
30
41
|
- lib/rgviz_rails.rb
|
31
42
|
- lib/rgviz_rails/executor.rb
|
@@ -35,16 +46,15 @@ files:
|
|
35
46
|
- lib/rgviz_rails/adapters/mysql_adapter.rb
|
36
47
|
- lib/rgviz_rails/adapters/postgresql_adapter.rb
|
37
48
|
- lib/rgviz_rails/adapters/sqlite_adapter.rb
|
38
|
-
-
|
49
|
+
- lib/rgviz_rails/init.rb
|
39
50
|
- spec/blueprints.rb
|
40
51
|
- spec/spec.opts
|
41
52
|
- spec/spec_helper.rb
|
42
53
|
- spec/models/city.rb
|
43
54
|
- spec/models/country.rb
|
44
55
|
- spec/models/person.rb
|
45
|
-
- spec/models/pet.rb
|
46
56
|
- spec/rgviz/executor_spec.rb
|
47
|
-
- README
|
57
|
+
- README.rdoc
|
48
58
|
has_rdoc: true
|
49
59
|
homepage: http://code.google.com/p/rgviz-rails
|
50
60
|
licenses: []
|
@@ -59,7 +69,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
69
|
requirements:
|
60
70
|
- - ">="
|
61
71
|
- !ruby/object:Gem::Version
|
62
|
-
hash: 3
|
63
72
|
segments:
|
64
73
|
- 0
|
65
74
|
version: "0"
|
@@ -68,7 +77,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
77
|
requirements:
|
69
78
|
- - ">="
|
70
79
|
- !ruby/object:Gem::Version
|
71
|
-
hash: 3
|
72
80
|
segments:
|
73
81
|
- 0
|
74
82
|
version: "0"
|
data/README
DELETED
@@ -1,243 +0,0 @@
|
|
1
|
-
== Welcome to Rails
|
2
|
-
|
3
|
-
Rails is a web-application framework that includes everything needed to create
|
4
|
-
database-backed web applications according to the Model-View-Control pattern.
|
5
|
-
|
6
|
-
This pattern splits the view (also called the presentation) into "dumb" templates
|
7
|
-
that are primarily responsible for inserting pre-built data in between HTML tags.
|
8
|
-
The model contains the "smart" domain objects (such as Account, Product, Person,
|
9
|
-
Post) that holds all the business logic and knows how to persist themselves to
|
10
|
-
a database. The controller handles the incoming requests (such as Save New Account,
|
11
|
-
Update Product, Show Post) by manipulating the model and directing data to the view.
|
12
|
-
|
13
|
-
In Rails, the model is handled by what's called an object-relational mapping
|
14
|
-
layer entitled Active Record. This layer allows you to present the data from
|
15
|
-
database rows as objects and embellish these data objects with business logic
|
16
|
-
methods. You can read more about Active Record in
|
17
|
-
link:files/vendor/rails/activerecord/README.html.
|
18
|
-
|
19
|
-
The controller and view are handled by the Action Pack, which handles both
|
20
|
-
layers by its two parts: Action View and Action Controller. These two layers
|
21
|
-
are bundled in a single package due to their heavy interdependence. This is
|
22
|
-
unlike the relationship between the Active Record and Action Pack that is much
|
23
|
-
more separate. Each of these packages can be used independently outside of
|
24
|
-
Rails. You can read more about Action Pack in
|
25
|
-
link:files/vendor/rails/actionpack/README.html.
|
26
|
-
|
27
|
-
|
28
|
-
== Getting Started
|
29
|
-
|
30
|
-
1. At the command prompt, start a new Rails application using the <tt>rails</tt> command
|
31
|
-
and your application name. Ex: rails myapp
|
32
|
-
2. Change directory into myapp and start the web server: <tt>script/server</tt> (run with --help for options)
|
33
|
-
3. Go to http://localhost:3000/ and get "Welcome aboard: You're riding the Rails!"
|
34
|
-
4. Follow the guidelines to start developing your application
|
35
|
-
|
36
|
-
|
37
|
-
== Web Servers
|
38
|
-
|
39
|
-
By default, Rails will try to use Mongrel if it's are installed when started with script/server, otherwise Rails will use WEBrick, the webserver that ships with Ruby. But you can also use Rails
|
40
|
-
with a variety of other web servers.
|
41
|
-
|
42
|
-
Mongrel is a Ruby-based webserver with a C component (which requires compilation) that is
|
43
|
-
suitable for development and deployment of Rails applications. If you have Ruby Gems installed,
|
44
|
-
getting up and running with mongrel is as easy as: <tt>gem install mongrel</tt>.
|
45
|
-
More info at: http://mongrel.rubyforge.org
|
46
|
-
|
47
|
-
Say other Ruby web servers like Thin and Ebb or regular web servers like Apache or LiteSpeed or
|
48
|
-
Lighttpd or IIS. The Ruby web servers are run through Rack and the latter can either be setup to use
|
49
|
-
FCGI or proxy to a pack of Mongrels/Thin/Ebb servers.
|
50
|
-
|
51
|
-
== Apache .htaccess example for FCGI/CGI
|
52
|
-
|
53
|
-
# General Apache options
|
54
|
-
AddHandler fastcgi-script .fcgi
|
55
|
-
AddHandler cgi-script .cgi
|
56
|
-
Options +FollowSymLinks +ExecCGI
|
57
|
-
|
58
|
-
# If you don't want Rails to look in certain directories,
|
59
|
-
# use the following rewrite rules so that Apache won't rewrite certain requests
|
60
|
-
#
|
61
|
-
# Example:
|
62
|
-
# RewriteCond %{REQUEST_URI} ^/notrails.*
|
63
|
-
# RewriteRule .* - [L]
|
64
|
-
|
65
|
-
# Redirect all requests not available on the filesystem to Rails
|
66
|
-
# By default the cgi dispatcher is used which is very slow
|
67
|
-
#
|
68
|
-
# For better performance replace the dispatcher with the fastcgi one
|
69
|
-
#
|
70
|
-
# Example:
|
71
|
-
# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
|
72
|
-
RewriteEngine On
|
73
|
-
|
74
|
-
# If your Rails application is accessed via an Alias directive,
|
75
|
-
# then you MUST also set the RewriteBase in this htaccess file.
|
76
|
-
#
|
77
|
-
# Example:
|
78
|
-
# Alias /myrailsapp /path/to/myrailsapp/public
|
79
|
-
# RewriteBase /myrailsapp
|
80
|
-
|
81
|
-
RewriteRule ^$ index.html [QSA]
|
82
|
-
RewriteRule ^([^.]+)$ $1.html [QSA]
|
83
|
-
RewriteCond %{REQUEST_FILENAME} !-f
|
84
|
-
RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
|
85
|
-
|
86
|
-
# In case Rails experiences terminal errors
|
87
|
-
# Instead of displaying this message you can supply a file here which will be rendered instead
|
88
|
-
#
|
89
|
-
# Example:
|
90
|
-
# ErrorDocument 500 /500.html
|
91
|
-
|
92
|
-
ErrorDocument 500 "<h2>Application error</h2>Rails application failed to start properly"
|
93
|
-
|
94
|
-
|
95
|
-
== Debugging Rails
|
96
|
-
|
97
|
-
Sometimes your application goes wrong. Fortunately there are a lot of tools that
|
98
|
-
will help you debug it and get it back on the rails.
|
99
|
-
|
100
|
-
First area to check is the application log files. Have "tail -f" commands running
|
101
|
-
on the server.log and development.log. Rails will automatically display debugging
|
102
|
-
and runtime information to these files. Debugging info will also be shown in the
|
103
|
-
browser on requests from 127.0.0.1.
|
104
|
-
|
105
|
-
You can also log your own messages directly into the log file from your code using
|
106
|
-
the Ruby logger class from inside your controllers. Example:
|
107
|
-
|
108
|
-
class WeblogController < ActionController::Base
|
109
|
-
def destroy
|
110
|
-
@weblog = Weblog.find(params[:id])
|
111
|
-
@weblog.destroy
|
112
|
-
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
The result will be a message in your log file along the lines of:
|
117
|
-
|
118
|
-
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1
|
119
|
-
|
120
|
-
More information on how to use the logger is at http://www.ruby-doc.org/core/
|
121
|
-
|
122
|
-
Also, Ruby documentation can be found at http://www.ruby-lang.org/ including:
|
123
|
-
|
124
|
-
* The Learning Ruby (Pickaxe) Book: http://www.ruby-doc.org/docs/ProgrammingRuby/
|
125
|
-
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
|
126
|
-
|
127
|
-
These two online (and free) books will bring you up to speed on the Ruby language
|
128
|
-
and also on programming in general.
|
129
|
-
|
130
|
-
|
131
|
-
== Debugger
|
132
|
-
|
133
|
-
Debugger support is available through the debugger command when you start your Mongrel or
|
134
|
-
Webrick server with --debugger. This means that you can break out of execution at any point
|
135
|
-
in the code, investigate and change the model, AND then resume execution!
|
136
|
-
You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
|
137
|
-
Example:
|
138
|
-
|
139
|
-
class WeblogController < ActionController::Base
|
140
|
-
def index
|
141
|
-
@posts = Post.find(:all)
|
142
|
-
debugger
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
So the controller will accept the action, run the first line, then present you
|
147
|
-
with a IRB prompt in the server window. Here you can do things like:
|
148
|
-
|
149
|
-
>> @posts.inspect
|
150
|
-
=> "[#<Post:0x14a6be8 @attributes={\"title\"=>nil, \"body\"=>nil, \"id\"=>\"1\"}>,
|
151
|
-
#<Post:0x14a6620 @attributes={\"title\"=>\"Rails you know!\", \"body\"=>\"Only ten..\", \"id\"=>\"2\"}>]"
|
152
|
-
>> @posts.first.title = "hello from a debugger"
|
153
|
-
=> "hello from a debugger"
|
154
|
-
|
155
|
-
...and even better is that you can examine how your runtime objects actually work:
|
156
|
-
|
157
|
-
>> f = @posts.first
|
158
|
-
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
|
159
|
-
>> f.
|
160
|
-
Display all 152 possibilities? (y or n)
|
161
|
-
|
162
|
-
Finally, when you're ready to resume execution, you enter "cont"
|
163
|
-
|
164
|
-
|
165
|
-
== Console
|
166
|
-
|
167
|
-
You can interact with the domain model by starting the console through <tt>script/console</tt>.
|
168
|
-
Here you'll have all parts of the application configured, just like it is when the
|
169
|
-
application is running. You can inspect domain models, change values, and save to the
|
170
|
-
database. Starting the script without arguments will launch it in the development environment.
|
171
|
-
Passing an argument will specify a different environment, like <tt>script/console production</tt>.
|
172
|
-
|
173
|
-
To reload your controllers and models after launching the console run <tt>reload!</tt>
|
174
|
-
|
175
|
-
== dbconsole
|
176
|
-
|
177
|
-
You can go to the command line of your database directly through <tt>script/dbconsole</tt>.
|
178
|
-
You would be connected to the database with the credentials defined in database.yml.
|
179
|
-
Starting the script without arguments will connect you to the development database. Passing an
|
180
|
-
argument will connect you to a different database, like <tt>script/dbconsole production</tt>.
|
181
|
-
Currently works for mysql, postgresql and sqlite.
|
182
|
-
|
183
|
-
== Description of Contents
|
184
|
-
|
185
|
-
app
|
186
|
-
Holds all the code that's specific to this particular application.
|
187
|
-
|
188
|
-
app/controllers
|
189
|
-
Holds controllers that should be named like weblogs_controller.rb for
|
190
|
-
automated URL mapping. All controllers should descend from ApplicationController
|
191
|
-
which itself descends from ActionController::Base.
|
192
|
-
|
193
|
-
app/models
|
194
|
-
Holds models that should be named like post.rb.
|
195
|
-
Most models will descend from ActiveRecord::Base.
|
196
|
-
|
197
|
-
app/views
|
198
|
-
Holds the template files for the view that should be named like
|
199
|
-
weblogs/index.html.erb for the WeblogsController#index action. All views use eRuby
|
200
|
-
syntax.
|
201
|
-
|
202
|
-
app/views/layouts
|
203
|
-
Holds the template files for layouts to be used with views. This models the common
|
204
|
-
header/footer method of wrapping views. In your views, define a layout using the
|
205
|
-
<tt>layout :default</tt> and create a file named default.html.erb. Inside default.html.erb,
|
206
|
-
call <% yield %> to render the view using this layout.
|
207
|
-
|
208
|
-
app/helpers
|
209
|
-
Holds view helpers that should be named like weblogs_helper.rb. These are generated
|
210
|
-
for you automatically when using script/generate for controllers. Helpers can be used to
|
211
|
-
wrap functionality for your views into methods.
|
212
|
-
|
213
|
-
config
|
214
|
-
Configuration files for the Rails environment, the routing map, the database, and other dependencies.
|
215
|
-
|
216
|
-
db
|
217
|
-
Contains the database schema in schema.rb. db/migrate contains all
|
218
|
-
the sequence of Migrations for your schema.
|
219
|
-
|
220
|
-
doc
|
221
|
-
This directory is where your application documentation will be stored when generated
|
222
|
-
using <tt>rake doc:app</tt>
|
223
|
-
|
224
|
-
lib
|
225
|
-
Application specific libraries. Basically, any kind of custom code that doesn't
|
226
|
-
belong under controllers, models, or helpers. This directory is in the load path.
|
227
|
-
|
228
|
-
public
|
229
|
-
The directory available for the web server. Contains subdirectories for images, stylesheets,
|
230
|
-
and javascripts. Also contains the dispatchers and the default HTML files. This should be
|
231
|
-
set as the DOCUMENT_ROOT of your web server.
|
232
|
-
|
233
|
-
script
|
234
|
-
Helper scripts for automation and generation.
|
235
|
-
|
236
|
-
test
|
237
|
-
Unit and functional tests along with fixtures. When using the script/generate scripts, template
|
238
|
-
test files will be generated for you and placed in this directory.
|
239
|
-
|
240
|
-
vendor
|
241
|
-
External libraries that the application depends on. Also includes the plugins subdirectory.
|
242
|
-
If the app has frozen rails, those gems also go here, under vendor/rails/.
|
243
|
-
This directory is in the load path.
|
data/spec/models/pet.rb
DELETED