dynamic_reports 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,2 +1,9 @@
1
+ 0.0.3
2
+ - Added jewler for github love.
3
+ 0.0.2
4
+ - Github compaitble gemspec
5
+ - Passing tests
6
+ 0.0.1
7
+ - Template fixups
1
8
  0.0.0
2
- - Initial dynamic reports codebase.
9
+ - Initial dynamic reports codebase.
data/README CHANGED
@@ -1,7 +1,7 @@
1
1
  = Dynamic Reports
2
2
 
3
3
  A dynamic reporting engine for Ruby / Rails
4
-
4
+
5
5
  == Reports
6
6
 
7
7
  The dynamic reports gem was created to fill a HUGE hole that we felt existed in the
@@ -114,32 +114,90 @@
114
114
  label_column "created_at"
115
115
  end
116
116
 
117
- == Stylizing
118
-
119
- The reports are, by default, stylized with an inline style sheet. The styles produce a nicely formatted grid with
120
- a white on black header row and black on white columns with a gray border througout.
117
+ == External Links
121
118
 
122
- You can create your own styles by simply adding a class_name object to the report definition as such:
123
-
124
- class OrdersReport < DynamicReports::Report
125
- title "Orders Report"
126
- subtitle "All orders recorded in database"
127
- columns :total, :created_at
119
+ Dynamic Reports supports linking from any column within your table. To add a link to a column, add the following
120
+ to a report definition:
121
+
122
+ link :column, url
123
+
124
+ For example:
125
+
126
+ class OrdersReport < DynamicReports::Report
127
+ title "Orders Report"
128
+ subtitle "All orders recorded in database"
129
+ columns :total, :created_at
130
+
131
+ link :total, '/report/daily_sales'
132
+ end
133
+
134
+ You can also pass parameters to the URL based on values from the underlying model. To pass a parameter, surround the
135
+ field name with {}. The parameter does NOT need to be a displayed, column. For example, you might want to pass
136
+ an external link an ID column but not display this column on the table.
137
+
138
+ For example:
139
+
140
+ class OrdersReport < DynamicReports::Report
141
+ title "Orders Report"
142
+ subtitle "All orders recorded in database"
143
+ columns :total, :created_at
144
+
145
+ link :total, '/report/item_sales?id={id}' # => Will substitute ID for the value of ID associated with that record
146
+ end
147
+
148
+
149
+ == Subreports
128
150
 
129
- class_name "my_class_name"
130
- end
151
+ Dynamic Reports supports the display of a sub-report within any report. This is accomplished using the jQuery library
152
+ available at http://www.jquery.com.
153
+
154
+ Sub-reports are created using the same definition format that you would use to create a standard report. The only
155
+ difference is that it is displayed INLINE when an associated link is clicked.
156
+
157
+ A sub-report is defined using the same format as a LINK above, but is labeled as:
158
+
159
+ subreport :column, url
160
+
161
+ For example, if you wanted to show all sales and allow a user to click on a specific item to see all historic sales
162
+ inline for just that item, you would do the following:
163
+
164
+ IN CONTROLLER:
165
+
166
+ def orders
167
+ @orders = Order.find(:all, :limit => 25)
168
+ render :text => OrdersReport.on(@orders).to_html, :layout => "application"
169
+ end
170
+
171
+ def item_sales
172
+ @item_orders = Order.find_by_id(params[:id])
173
+ render :text => ItemSales.on(@orders).to_html, :layout => "application"
174
+ end
175
+
176
+ REPORT DEFINITIONS
177
+
178
+ class OrdersReport < DynamicReports::Report
179
+ title "Orders Report"
180
+ subtitle "All orders recorded in database"
181
+ columns :total, :created_at
182
+
183
+ subreport :total, '/report/item_sales?id={id}' # => Will substitute ID for the value of ID associated with that record
184
+ end
185
+
186
+ class ItemSales < DynamicReports::Report
187
+ columns :item, :price, :created_at
188
+ end
189
+
190
+ Subreports can also be nested.
191
+
192
+ == Rails Usage
131
193
 
132
- This will cause DR to simply not include the inline style. From there you can customer the styles using the
133
- following sub-classes for your class name, for example:
194
+ The gem includes a stylesheet and javascript based on jQuery. To add both to your Rails project,
195
+ simply type "drsetup" from the project root. This will add:
134
196
 
135
- .my_class_name .report_title {}
136
- .my_class_name .report_subtitle {}
137
- .my_class_name table tr th {}
138
- .my_class_name table tr td {}
139
- .my_class_name .report_charts {} // all charts are displayed within this div
140
- .my_class_name .report_chart {} // represents an individual chart
197
+ public/stylesheets/dynamic_reports.css (controls style of dynamic reports)
198
+ public/javascripts/dynamic_reports.js (controls display of subreports)
141
199
 
142
- == Rails Usage
200
+ You can then modify these files as you see fit.
143
201
 
144
202
  Inside the initializer block in config/environment.rb
145
203
 
@@ -179,7 +237,7 @@
179
237
  == Thanks To
180
238
 
181
239
  * Daniel Neighman
182
- * Kenneth Kalmer (And his friend :))
240
+ * Kenneth Kalmer & Nic Young
183
241
  * Yehuda Katz
184
242
 
185
243
  For their encouragement, feedback and advise.
@@ -0,0 +1,247 @@
1
+ = Dynamic Reports
2
+
3
+ A dynamic reporting engine for Ruby / Rails
4
+
5
+ == Reports
6
+
7
+ The dynamic reports gem was created to fill a HUGE hole that we felt existed in the
8
+ Ruby community - the ability to QUICKLY create stylized admin reports and charts for
9
+ people to use to view key metrics and data.
10
+
11
+ Sample uses include the ability to quickly display sales data if your an eShop, our
12
+ site metrics if you are recording your own site visits, or user feedback if you are storing
13
+ feedback in a model somewhere.
14
+
15
+ Basically, with DR you can create a stylized table of ANY information found in a model
16
+ (kind of like looking at the grid output from a GUI query analyzer) as well as add Google
17
+ Charts API powered line, pie, bar or column charts of any numeric data. All this can
18
+ be done by simply creating a report definition and feeding it your data.
19
+
20
+ While this library is usable in any Ruby application it was made mainly with Rails in mind.
21
+ Suppose we have an online store and we wish to add reporting to the admin area quickly and easily.
22
+ First we define a report in app/reports/orders_report.rb, something like:
23
+
24
+ class OrdersReport < DynamicReports::Report
25
+ title "Orders Report"
26
+ subtitle "All orders recorded in database"
27
+ columns :total, :created_at
28
+ end
29
+
30
+ Then in our admin/reports controller (this can be any controller) we define an action to deliver the report:
31
+
32
+ def orders
33
+ @orders = Order.find(:all, :limit => 25)
34
+ render :text => OrdersReport.on(@orders).to_html, :layout => "application"
35
+ end
36
+
37
+ This will render an html table containing some basic styling and containing the columns 'total' and 'created_at' from the order objects.
38
+ Note that the report Title will be "Orders Report" and it's name will be :orders_report
39
+ Report#on expects that it receives an object that responds to #each and
40
+ That each object that it iterates over is either a
41
+ * An object
42
+ * A Hash
43
+ that responds to a method / has keys for each column defined within the report.
44
+
45
+
46
+ Templating engines may also be specified, currently :erb and :haml are supported (we will soon be adding :csv and :pdf) like so:
47
+
48
+ render :text => OrdersReport.on(@orders).to_html(:engine => :haml), :layout => "application"
49
+
50
+ Note that erb is the default templating engine since it is available by default in Ruby.
51
+
52
+ Now let us extend our report definition to specify a template to use!
53
+
54
+ class OrdersReport < DynamicReports::Report
55
+ title "Orders Report"
56
+ subtitle "All orders recorded in database"
57
+ columns :total, :created_at
58
+
59
+ template :my_custom_template
60
+ end
61
+
62
+ This will look in app/views/reports/ for a template named "my_custom_template.html.erb" by default.
63
+ If you specify :engine => :haml then it will look for "my_custom_template.html.haml"
64
+
65
+ If you happen to have your report templates in a different location you can specify this as follows:
66
+
67
+ class OrdersReport < DynamicReports::Report
68
+ title "Orders Report"
69
+ subtitle "All orders recorded in database"
70
+ columns :total, :created_at
71
+
72
+ template :my_custom_template
73
+ views "app/views/admin/reports/"
74
+ end
75
+
76
+ And DynamicReports will look for the specified template in app/views/reports as well as app/views/admin/reports.
77
+
78
+ It is also worth pointing out that you can have as many dynamic reports in a view as you wish, simply include
79
+ each report render where desired within the view.
80
+
81
+ == Charts
82
+
83
+ Charts can be defined on a report easily. Let's say we wish to chart the total versus the item quantity sold for our Orders Report exmaple:
84
+
85
+ class OrdersReport < DynamicReports::Report
86
+ title "Orders Report"
87
+ subtitle "All orders recorded in database"
88
+ columns :total, :created_at
89
+
90
+ chart :total_vs_quantity do
91
+ columns :total, :quantity
92
+ label_column "created_at"
93
+ end
94
+ end
95
+
96
+ This will render a *line* chart by default displaying the columns total and quantity.
97
+ Chart types may be specified easily:
98
+
99
+ type :bar
100
+
101
+ Available chart types are:
102
+
103
+ * :line (default)
104
+ * :bar
105
+ * :pie
106
+
107
+ Since DynamicReport's charts utilize the Google Chart API, you can easily extend each chart by passing a hash of chart options as part
108
+ of the block. The options are appended onto the request to the API so they should follow the Google's API commands (http://code.google.com/apis/chart/)
109
+
110
+ For example, to add min, max and average labels to the example chart, you would do something like this:
111
+
112
+ chart :total_vs_quantity, {:chxt => "r", :chxl => "0:|min|average|max"} do
113
+ columns :total, :quantity
114
+ label_column "created_at"
115
+ end
116
+
117
+ == External Links
118
+
119
+ Dynamic Reports supports linking from any column within your table. To add a link to a column, add the following
120
+ to a report definition:
121
+
122
+ link :column, url
123
+
124
+ For example:
125
+
126
+ class OrdersReport < DynamicReports::Report
127
+ title "Orders Report"
128
+ subtitle "All orders recorded in database"
129
+ columns :total, :created_at
130
+
131
+ link :total, '/report/daily_sales'
132
+ end
133
+
134
+ You can also pass parameters to the URL based on values from the underlying model. To pass a parameter, surround the
135
+ field name with {}. The parameter does NOT need to be a displayed, column. For example, you might want to pass
136
+ an external link an ID column but not display this column on the table.
137
+
138
+ For example:
139
+
140
+ class OrdersReport < DynamicReports::Report
141
+ title "Orders Report"
142
+ subtitle "All orders recorded in database"
143
+ columns :total, :created_at
144
+
145
+ link :total, '/report/item_sales?id={id}' # => Will substitute ID for the value of ID associated with that record
146
+ end
147
+
148
+
149
+ == Subreports
150
+
151
+ Dynamic Reports supports the display of a sub-report within any report. This is accomplished using the jQuery library
152
+ available at http://www.jquery.com.
153
+
154
+ Sub-reports are created using the same definition format that you would use to create a standard report. The only
155
+ difference is that it is displayed INLINE when an associated link is clicked.
156
+
157
+ A sub-report is defined using the same format as a LINK above, but is labeled as:
158
+
159
+ subreport :column, url
160
+
161
+ For example, if you wanted to show all sales and allow a user to click on a specific item to see all historic sales
162
+ inline for just that item, you would do the following:
163
+
164
+ IN CONTROLLER:
165
+
166
+ def orders
167
+ @orders = Order.find(:all, :limit => 25)
168
+ render :text => OrdersReport.on(@orders).to_html, :layout => "application"
169
+ end
170
+
171
+ def item_sales
172
+ @item_orders = Order.find_by_id(params[:id])
173
+ render :text => ItemSales.on(@orders).to_html, :layout => "application"
174
+ end
175
+
176
+ REPORT DEFINITIONS
177
+
178
+ class OrdersReport < DynamicReports::Report
179
+ title "Orders Report"
180
+ subtitle "All orders recorded in database"
181
+ columns :total, :created_at
182
+
183
+ subreport :total, '/report/item_sales?id={id}' # => Will substitute ID for the value of ID associated with that record
184
+ end
185
+
186
+ class ItemSales < DynamicReports::Report
187
+ columns :item, :price, :created_at
188
+ end
189
+
190
+ Subreports can also be nested.
191
+
192
+ == Rails Usage
193
+
194
+ The gem includes a stylesheet and javascript based on jQuery. To add both to your Rails project,
195
+ simply type "drsetup" from the project root. This will add:
196
+
197
+ public/stylesheets/dynamic_reports.css (controls style of dynamic reports)
198
+ public/javascripts/dynamic_reports.js (controls display of subreports)
199
+
200
+ You can then modify these files as you see fit.
201
+
202
+ Inside the initializer block in config/environment.rb
203
+
204
+ config.gem "dynamic_reports"
205
+
206
+ Then define your reports (as exampled above) in app/reports/*_report.rb
207
+ If you would like to customize the default report simply create your report templates
208
+ within app/views/reports/*_report.<content-type>.<engine>.
209
+
210
+ Two Rails features that we are currently working on are:
211
+
212
+ * generator
213
+ * render extensions
214
+
215
+ == Optional Dependencies
216
+
217
+ We are currently examining solutions for csv, pdf and charting.
218
+
219
+ * Fastercsv # csv
220
+ * Prawn # pdf
221
+ * flying saucer # html => PDF - if jRuby available
222
+ * amcharts # Charting, note that default is built in google charts.
223
+
224
+ These will be defined/implemented using DynamicReports plugin API (not implemented yet)
225
+ Which allows for user defined plugins of arbitrary types beyond html,csv,pdf,xml
226
+
227
+ == Contact / Feedback
228
+
229
+ If you have any suggestions on improvement please send us an email.
230
+
231
+ == Authors (alphabetically)
232
+
233
+ Joshua Lippiner (jlippiner@gmail.com)
234
+
235
+ Wayne E. Seguin (wayneeseguin@gmail.com, irc: wayneeseguin)
236
+
237
+ == Thanks To
238
+
239
+ * Daniel Neighman
240
+ * Kenneth Kalmer & Nic Young
241
+ * Yehuda Katz
242
+
243
+ For their encouragement, feedback and advise.
244
+
245
+ == Source
246
+ http://github.com/wayneeseguin/dynamic_reports
247
+
@@ -0,0 +1,62 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{dynamic_reports}
5
+ s.version = "0.0.4"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Wayne E. Seguin", "Joshua Lippiner"]
9
+ s.date = %q{2009-07-01}
10
+ s.description = %q{Dynamic Ruby Reporting Engine with support for Charts.}
11
+ s.email = %q{wayneeseguin@gmail.com, jlippiner@gmail.com}
12
+ s.extra_rdoc_files = [
13
+ "README",
14
+ "README.rdoc"
15
+ ]
16
+ s.files = [
17
+ "HISTORY",
18
+ "README",
19
+ "dynamic_reports.gemspec",
20
+ "lib/dynamic_reports.rb",
21
+ "lib/dynamic_reports/charts.rb",
22
+ "lib/dynamic_reports/reports.rb",
23
+ "lib/dynamic_reports/templates.rb",
24
+ "lib/dynamic_reports/vendor/google_chart.rb",
25
+ "lib/dynamic_reports/vendor/google_chart/bar_chart.rb",
26
+ "lib/dynamic_reports/vendor/google_chart/base.rb",
27
+ "lib/dynamic_reports/vendor/google_chart/financial_line_chart.rb",
28
+ "lib/dynamic_reports/vendor/google_chart/line_chart.rb",
29
+ "lib/dynamic_reports/vendor/google_chart/pie_chart.rb",
30
+ "lib/dynamic_reports/vendor/google_chart/scatter_chart.rb",
31
+ "lib/dynamic_reports/vendor/google_chart/venn_diagram.rb",
32
+ "lib/dynamic_reports/views.rb",
33
+ "lib/dynamic_reports/views/default_layout.html.erb",
34
+ "lib/dynamic_reports/views/default_report.html.erb",
35
+ "lib/dynamic_reports/views/default_report.html.haml"
36
+ ]
37
+ s.homepage = %q{http://dynamicreports.rubyforge.org/}
38
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubyforge_project = %q{dynamicreports}
41
+ s.rubygems_version = %q{1.3.3}
42
+ s.summary = %q{Dynamic Ruby Reporting Engine with support for Charts}
43
+ s.test_files = [
44
+ "test/dynamic_reports/charts_test.rb",
45
+ "test/dynamic_reports/reports_test.rb",
46
+ "test/dynamic_reports/templates_test.rb",
47
+ "test/dynamic_reports/views_test.rb",
48
+ "test/dynamic_reports.rb",
49
+ "test/factories/records.rb",
50
+ "test/test_helper.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
+ else
59
+ end
60
+ else
61
+ end
62
+ end
@@ -22,7 +22,14 @@ require "dynamic_reports/vendor/google_chart"
22
22
  # require "dynamic_reports/rails"
23
23
  # For now placing the code right here:
24
24
  if defined?(Rails)
25
- # Load all defined reports.
25
+ # ath_to_lib = File.join(Rails.root, "app") #adjust if necessary
26
+ # path_to_tree = "#{path_to_lib}/reports"
27
+ # Dir["#{path_to_tree}/**/*.rb"].each { |fn|
28
+ # fn =~ /^#{Regexp.escape(path_to_lib)}\/(.*)\.rb$/
29
+ # require $1
30
+ # }
31
+
32
+ # Load all defined reports.
26
33
  # Question: How to get Rails to reload files other than ones matching the requested constant...
27
34
  #Dir.glob("#{File.join(Rails.root, "app", "reports")}/*.rb").each { |file| require file }
28
35
  ActiveSupport::Dependencies.load_paths << File.join(Rails.root, "app", "reports")
@@ -44,4 +51,3 @@ if defined?(Rails)
44
51
 
45
52
  # TODO: Generator
46
53
  end
47
-
@@ -8,7 +8,7 @@ module DynamicReports
8
8
  class Report
9
9
  @@default_engine = "erb"
10
10
 
11
- attr_accessor :name, :title, :sub_title, :columns, :charts, :records, :template, :class_name, :styles
11
+ attr_accessor :name, :title, :sub_title, :columns, :charts, :records, :template, :class_name, :styles, :links
12
12
 
13
13
  # views accessor, array of view paths.
14
14
  def views
@@ -24,7 +24,13 @@ module DynamicReports
24
24
  # Views setter and accessor.
25
25
  def views(*array)
26
26
  @views ||= ["#{File::dirname(File::expand_path(__FILE__))}/views/"]
27
- array ? @views += array : @views
27
+ unless array.empty?
28
+ @views.unshift(array)
29
+ @views.flatten!
30
+ @views.uniq!
31
+ else
32
+ @views
33
+ end
28
34
  end
29
35
 
30
36
  # class level options accessor
@@ -100,7 +106,11 @@ module DynamicReports
100
106
  # OrdersReport.template # => :orders
101
107
  #
102
108
  def template(value = nil)
103
- value ? options[:template] = value : options[:template]
109
+ if value
110
+ @template = value
111
+ options[:template] = @template
112
+ end
113
+ @template ||= nil
104
114
  end
105
115
 
106
116
  # Accessor for columns
@@ -160,6 +170,50 @@ module DynamicReports
160
170
  chart_options = chart_options.shift || {}
161
171
  charts(Chart.configure(name, chart_options, &block))
162
172
  end
173
+
174
+ # Return an array of links defined for the report.
175
+ def links(object=nil)
176
+ options[:links] ||= []
177
+ options[:links] << object if object
178
+ options[:links]
179
+ end
180
+
181
+ # Define a link for the report
182
+ #
183
+ # Pass parameters within {}. Parameters are replaced with the row values
184
+ # from passed records. You do NOT need to include a parameter value as a
185
+ # report column for it to be used in a link. For example, you might
186
+ # want to generate a link with an ID field in it but not display that id
187
+ # in the actual report. Just include {id} to do this.
188
+ #
189
+ # Example:
190
+ #
191
+ # link :visits, '/reports/{visit}/details?date={recorded_at}'
192
+ #
193
+ def link(column, url, link_options=nil)
194
+ links({:column => column, :url => url, :link_options => link_options})
195
+ end
196
+
197
+ # Define an inline subreport for the report
198
+ #
199
+ # Pass parameters within {}. Parameters are replaced with the row values
200
+ # from passed records. You do NOT need to include a parameter value as a
201
+ # report column for it to be used in a link. For example, you might
202
+ # want to generate a subreport with an ID field in it but not display that id
203
+ # in the actual report. Just include {id} to do this.
204
+ #
205
+ # Example:
206
+ #
207
+ # subreport :visits, '/reports/{visit}/details?date={recorded_at}'
208
+ #
209
+ # The subreport should be created using the same report definition style
210
+ # that you use for any other report.
211
+ #
212
+ def subreport(column, url, link_options=nil)
213
+ link_options ||= {}
214
+ link_options.merge!({:class => 'sub_report_link'})
215
+ links({:column => column, :url => url, :link_options => link_options})
216
+ end
163
217
 
164
218
  # Method for instanciating a report instance on a set of given records.
165
219
  #
@@ -177,14 +231,6 @@ module DynamicReports
177
231
  def on(records)
178
232
  new(records, @options)
179
233
  end
180
-
181
- #--
182
- # Methods for definining a sub report
183
- #def link_column
184
- #end
185
- #def link_rows
186
- #end
187
-
188
234
  end
189
235
 
190
236
  # Instantiate the report on a set of records.
@@ -201,10 +247,15 @@ module DynamicReports
201
247
  def initialize(records, *new_options)
202
248
  new_options = new_options.shift || {}
203
249
  @records = records
204
- @views = []
205
- @views << new_options.delete(:views) if new_options[:views]
206
- @views += self.class.views
250
+
251
+ @views = self.class.views
252
+ @views.unshift(new_options.delete(:views)) if new_options[:views]
253
+ @views.flatten!
254
+ @views.uniq!
255
+
256
+ @template = self.class.template
207
257
  @template = new_options.delete(:template) if new_options[:template]
258
+
208
259
  @options = self.class.options.merge!(new_options)
209
260
  @options.each_pair do |key,value|
210
261
  if key == "chart"
@@ -233,6 +284,7 @@ module DynamicReports
233
284
  options = (options.shift || {}).merge!(@options || {})
234
285
  # todo: if rails is loaded set the default engine: dynamicreports::report.default_engine
235
286
  engine = options.delete(:engine) || @@default_engine
287
+ options[:template] = self.class.template
236
288
  view.__send__("#{engine}", options)
237
289
  end
238
290
 
@@ -32,7 +32,7 @@ module DynamicReports
32
32
 
33
33
  # TODO: Add Report Helpers for injection
34
34
  def titleize(object)
35
- object.to_s.split('_').each{ |word| word.capitalize! }.join(' ')
35
+ object.to_s.split('_').each{ |word| word.capitalize! }.join(' ')
36
36
  end
37
37
 
38
38
  def commify(object)
@@ -43,9 +43,44 @@ module DynamicReports
43
43
  end
44
44
  end
45
45
 
46
+ def linkcheck(record, column_object)
47
+ val = ''
48
+
49
+ if column_object.is_a?(Hash)
50
+ if column_object.keys.include?(:column)
51
+ column = column_object[:column]
52
+ else
53
+ column = column_object.keys.first # => Josh shortcut :)
54
+ end
55
+ else
56
+ column = column_object
57
+ end
58
+
59
+ report.links.each do |link|
60
+ if link[:column] == column
61
+ url = link[:url]
62
+ url.scan(/\{(\w+)\}/).each do |parameter|
63
+ parameter = parameter.to_s
64
+ url = url.gsub("{#{parameter}}", CGI::escape(get_record_value(record, parameter.to_sym).to_s))
65
+ end
66
+
67
+ url_attribute_str = []
68
+ link[:link_options].keys.each do |key|
69
+ url_attribute_str << "#{key}='#{link[:link_options][key]}'"
70
+ end if link[:link_options]
71
+
72
+ val = "<a href='#{url}' #{url_attribute_str.join(' ')}>#{get_record_value(record,column)}</a>"
73
+ break
74
+ end if link[:column]
75
+ end if report.links
76
+
77
+ val.blank? ? get_record_value(record,column) : val
78
+ end
79
+
80
+
46
81
  def chart_url(chart,report)
47
82
  columns = chart.columns ? chart.columns : report.columns
48
- chart_type = chart.type.nil? ? :line : chart.type.to_sym
83
+ chart_type = chart.type.nil? ? :line : chart.type.to_sym
49
84
  case chart_type
50
85
  when :line
51
86
  Charts.line_chart(chart,columns,report)
@@ -62,7 +97,7 @@ module DynamicReports
62
97
  end
63
98
 
64
99
  private
65
-
100
+
66
101
  def render(engine, template, options={}, locals={})
67
102
  # merge app-level options
68
103
  options = self.class.send(engine).merge(options) if self.class.respond_to?(engine)
@@ -74,7 +109,7 @@ module DynamicReports
74
109
  content_type = options.delete(:content_type)
75
110
  locals = options.delete(:locals) || locals || {}
76
111
  locals.merge!(:report => @report, :options => options || {})
77
-
112
+
78
113
  # render template
79
114
  data, options[:filename], options[:line] = lookup_template(engine, template, views, content_type)
80
115
  output = __send__("render_#{engine}", template, data, options, locals)
@@ -101,7 +136,7 @@ module DynamicReports
101
136
  lookup_template(engine, cached[:template], views, content_type, cached[:filename], cached[:line])
102
137
  else
103
138
  filename = "#{template}.#{content_type}.#{engine}"
104
- dir = views.to_a.detect do |view|
139
+ dir = views.to_a.detect do |view|
105
140
  ::File.exists?(::File.join(view, filename))
106
141
  end
107
142
  if dir
@@ -172,7 +207,15 @@ module DynamicReports
172
207
  require engine.downcase
173
208
  end
174
209
 
175
-
176
-
210
+ def get_record_value(record, column)
211
+ if record.is_a?(Hash)
212
+ record[column]
213
+ elsif record.respond_to?(column.to_sym)
214
+ record.send(column.to_sym)
215
+ else
216
+ column
217
+ end
218
+ end
219
+
177
220
  end
178
221
  end
@@ -1,34 +1,3 @@
1
- <% if report.class_name.nil? %>
2
- <style type="text/css">
3
- .dynamic_report .report_title {
4
- font-size:16pt;
5
- font-weight:bold;
6
- margin:10px 0px;
7
- }
8
- .dynamic_report .report_subtitle {
9
- font-size:14pt;
10
- color:black;
11
- margin:10px 0px;
12
- }
13
- .dynamic_report table tr th {
14
- color: white;
15
- background: gray;
16
- padding:5px;
17
- }
18
- .dynamic_report table tr td {
19
- border: 1px solid black;
20
- padding:3px 15px;
21
- }
22
- .dynamic_report .report_charts {
23
- width:100%;
24
- }
25
-
26
- .dynamic_report .report_chart {
27
- margin:15px;
28
- }
29
- </style>
30
- <% end %>
31
-
32
1
  <div id="<%= report.class_name %>" class="dynamic_report">
33
2
  <%= "<div class='report_title'>#{report.title}</div>" if report.title %>
34
3
  <%= "<div class='report_subtitle'>#{report.sub_title}</div>" if report.sub_title %>
@@ -36,38 +5,38 @@
36
5
  <thead class="report_header">
37
6
  <tr class="report_header_row">
38
7
  <% report.columns.each do |column| %>
39
- <th>
40
- <%= options[:titleize] ? titleize(column) : column %>
41
- </th>
8
+ <th>
9
+ <% if column.is_a?(Hash) %>
10
+ <% if column.keys.include?(:heading) %>
11
+ <%= options[:titleize] ? titleize(column[:heading]) : column[:heading] %>
12
+ <% else %>
13
+ <%= options[:titleize] ? titleize(column.values.first) : column.values.first %>
14
+ <% end %>
15
+ <% else %>
16
+ <%= options[:titleize] ? titleize(column) : column %>
17
+ <% end %>
18
+ </th>
42
19
  <% end %>
43
20
  </tr>
44
21
  </thead>
45
22
  <tbody class="report_body">
46
23
  <% report.records.each do |record| %>
47
- <tr class="report_row">
48
- <% report.columns.each do |column| %>
49
- <td>
50
- <% if record.is_a?(Hash) %>
51
- <%= (options[:commas] == true) ? commify(record[column]) : record[column] %>
52
- <% elsif record.respond_to?(column.to_sym) %>
53
- <%= (options[:commas] == true) ? commify(record.send(column.to_sym)) : record.send(column.to_sym) %>
54
- <% else %>
55
- <%= column %>
56
- <% end %>
57
- </td>
58
- <% end %>
59
- </tr>
24
+ <tr class="report_row">
25
+ <% report.columns.each do |column| %>
26
+ <td>
27
+ <%= (options[:commas] == true) ? commify(linkcheck(record,column)) : linkcheck(record,column) %>
28
+ </td>
29
+ <% end %>
30
+ </tr>
60
31
  <% end %>
61
32
  </tbody>
62
33
  </table>
63
-
34
+
64
35
  <div class="report_charts">
65
36
  <% report.charts.to_a.each do |chart| %>
66
- <span class="report_chart">
67
- <%= "<img src='#{chart_url(chart,report)}' alt='#{chart.name}'>" %>
68
- </span>
37
+ <span class="report_chart">
38
+ <%= "<img src='#{chart_url(chart,report)}' alt='#{chart.name}'>" %>
39
+ </span>
69
40
  <% end %>
70
41
  </div>
71
-
72
42
  </div>
73
-
@@ -1,31 +1,3 @@
1
- - if report.class_name.nil?
2
- %style{type => "text/css"}
3
- \.dynamic_report .report_title {
4
- font-size:16pt;
5
- font-weight:bold;
6
- margin:10px 0px;
7
- }
8
- \.dynamic_report .report_subtitle {
9
- font-size:14pt;
10
- color:black;
11
- margin:10px 0px;
12
- }
13
- \.dynamic_report table tr th {
14
- color: white;
15
- background: gray;
16
- padding:5px;
17
- }
18
- \.dynamic_report table tr td {
19
- border: 1px solid black;
20
- padding:3px 15px;
21
- }
22
- \.dynamic_report .report_charts {
23
- width:100%;
24
- }
25
- \.dynamic_report .report_chart {
26
- margin:15px;
27
- }
28
-
29
1
  .dynamic_report{ :id => report.class_name }
30
2
  - if report.title
31
3
  %h2.report_title
@@ -40,18 +12,19 @@
40
12
  %tr.report_header_row
41
13
  - report.columns.each do |column|
42
14
  %th
43
- = options[:titleize] ? titleize(column) : column
15
+ - if column.is_a?(Hash)
16
+ - if column.keys.include?(:heading)
17
+ = options[:titleize] ? titleize(column[:heading]) : column[:heading]
18
+ -else
19
+ = options[:titleize] ? titleize(column.values.first) : column.values.first
20
+ -else
21
+ = options[:titleize] ? titleize(column) : column
44
22
  %tbody.report_body
45
23
  - report.records.each do |record|
46
24
  %tr.report_row
47
25
  - report.columns.each do |column|
48
26
  %td
49
- - if record.is_a?(Hash)
50
- = (options[:commas] == true) ? commify(record[column]) : record[column]
51
- - elsif record.respond_to?(column.to_sym)
52
- = (options[:commas] == true) ? commify(record.send(column.to_sym)) : record.send(column.to_sym)
53
- - else
54
- = column
27
+ = (options[:commas] == true) ? commify(linkcheck(record,column)) : linkcheck(record,column)
55
28
 
56
29
  - if report.charts && report.charts.class === Hash
57
30
  - report.charts.each_pair do |name, chart|
@@ -59,4 +32,3 @@
59
32
  %h2
60
33
  = name
61
34
  %img{:src => chart_url(chart), :alt => name}
62
-
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "test/unit/testsuite"
4
+ require "test/unit/ui/console/testrunner"
5
+ #require "test/unit/color"
6
+ require "shoulda"
7
+ require "zentest"
8
+
9
+ # Libraries
10
+ require "dynamic_reports"
11
+ require "test/test_helper"
12
+
13
+ # Test files.
14
+ require "test/dynamic_reports/charts_test"
15
+ require "test/dynamic_reports/reports_test"
16
+ require "test/dynamic_reports/views_test"
17
+
18
+ require "test/unit"
@@ -0,0 +1,61 @@
1
+ class ChartsTest < Test::Unit::TestCase
2
+ context "charts" do
3
+ setup do
4
+ class TheReport < DynamicReports::Report
5
+ chart :the_chart_name, :an => "option", :another => "option" do
6
+ title "awesome report!"
7
+ type :svg
8
+ height "350"
9
+ width "250"
10
+ columns :pageviews
11
+ label_column "created_at"
12
+ end
13
+ chart :the_other_chart_name do
14
+ title "Some other chart"
15
+ end
16
+ end
17
+ end
18
+
19
+ should "allow specification of options to the chart beyond it's attributes." do
20
+ hash = {
21
+ :name => :the_chart_name,
22
+ :an => "option",
23
+ :type => :svg,
24
+ :another => "option",
25
+ :title=>"awesome report!",
26
+ :height=>"350",
27
+ :width=>"250",
28
+ :columns=>[:pageviews],
29
+ :label_column => "created_at"
30
+ }
31
+ assert_equal hash, TheReport.chart_with_name(:the_chart_name).options
32
+ end
33
+
34
+ should "allow setting their name" do
35
+ assert_equal :the_chart_name, TheReport.chart_with_name(:the_chart_name).name
36
+ end
37
+
38
+ should "allow setting their title" do
39
+ assert_equal "awesome report!", TheReport.chart_with_name(:the_chart_name).title
40
+ end
41
+
42
+ should "allow setting their type" do
43
+ assert_equal :svg, TheReport.chart_with_name(:the_chart_name).type
44
+ end
45
+
46
+ should "allow setting their width" do
47
+ assert_equal "250" ,TheReport.chart_with_name(:the_chart_name).width
48
+ end
49
+
50
+ should "allow setting their height" do
51
+ assert_equal "350", TheReport.chart_with_name(:the_chart_name).height
52
+ end
53
+
54
+ should "allow definition of multiple charts" do
55
+ #require "ruby-debug" ; debugger
56
+ assert_contains TheReport.charts[0].name, :the_chart_name
57
+ assert_contains TheReport.charts[1].name, :the_other_chart_name
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,77 @@
1
+ class ReportsTest < Test::Unit::TestCase
2
+ context "a report" do
3
+
4
+ setup do
5
+ class TheReport < DynamicReports::Report
6
+ name "The Report!"
7
+ title "Daily Report"
8
+ sub_title "Happens every day, ya!"
9
+ columns :recorded_at, :viewed_on, :pageviews_count, :visits_count, :conversions_count
10
+
11
+ chart :visits do
12
+ title "testing chart"
13
+ columns :visits
14
+ type "line"
15
+ end
16
+ end
17
+ @array_records = DynamicReports::Test::ArrayRecords.generate(TheReport, :count => 10)
18
+ @object_records = DynamicReports::Test::ObjectRecords.generate(TheReport, :count => 10)
19
+ end
20
+
21
+ should "allow setting the title of a report" do
22
+ assert_equal "Daily Report", TheReport.title
23
+ end
24
+
25
+ should "allow setting the sub title of a report" do
26
+ assert_equal "Happens every day, ya!", TheReport.sub_title
27
+ end
28
+
29
+ should "allow setting the columns to report on" do
30
+ assert_equal [:recorded_at, :viewed_on, :pageviews_count, :visits_count, :conversions_count], TheReport.columns
31
+ end
32
+
33
+ should "return an instantiated report .on is called" do
34
+ assert_kind_of DynamicReports::Report,
35
+ TheReport.on(@array_records)
36
+ end
37
+
38
+ should "An instantiated report should return html table report when to_html is called" do
39
+ assert_match(/<table class="report"/, TheReport.on(@array_records).to_html)
40
+ end
41
+
42
+ should "An instantiated report should contain the rendered chart that was defined" do
43
+ assert_match(/class="report_charts"/, TheReport.on(@array_records).to_html)
44
+ end
45
+
46
+ context "Report with templates and views specified" do
47
+ setup do
48
+ class SpecificTemplateReport < DynamicReports::Report
49
+ name "A report with specified template!"
50
+ title "Specify Template Report"
51
+ sub_title "template is 'other_template'"
52
+ columns :recorded_at, :viewed_on, :pageviews_count, :visits_count, :conversions_count
53
+ template :specific_template
54
+ views "#{File::dirname(File::expand_path(__FILE__))}/views/"
55
+ end
56
+ @array_records = DynamicReports::Test::ArrayRecords.generate(SpecificTemplateReport, :count => 10)
57
+ @object_records = DynamicReports::Test::ObjectRecords.generate(SpecificTemplateReport, :count => 10)
58
+ end
59
+
60
+ should "allow setting of the views directories" do
61
+ assert_contains SpecificTemplateReport.views,
62
+ "#{File::dirname(File::expand_path(__FILE__))}/views/"
63
+ end
64
+
65
+ should "the instance should have the same views as the class" do
66
+ assert_contains SpecificTemplateReport.on(@array_records).views,
67
+ "#{File::dirname(File::expand_path(__FILE__))}/views/"
68
+ end
69
+
70
+ should "render with the specified template, from the specified views directory" do
71
+ assert_equal "This is the specified template!",
72
+ SpecificTemplateReport.on(@array_records).to_html.strip
73
+ end
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,3 @@
1
+ class TemplatesTest < Test::Unit::TestCase
2
+
3
+ end
@@ -0,0 +1,5 @@
1
+ class ReportsTest < Test::Unit::TestCase
2
+ context "View" do
3
+
4
+ end
5
+ end
File without changes
@@ -0,0 +1,64 @@
1
+ require "ostruct"
2
+
3
+ module DynamicReports
4
+ module Test
5
+
6
+ class ArrayRecords
7
+ def self.generate(report, *options)
8
+ records = []
9
+ options = options.shift || {}
10
+ (0..(options[:count].to_i)).each do |index|
11
+ hash = {}
12
+ report.columns.each do |column|
13
+ hash[column.to_s] =
14
+ case column.to_s
15
+ when /_at$/
16
+ (DateTime.now-100)+index
17
+ when /_on$/
18
+ (Date.today-100)+index
19
+ when /_id$/,/_count$/
20
+ rand(10000)
21
+ else
22
+ column
23
+ end
24
+ end
25
+ records << hash
26
+ end
27
+ records
28
+ end
29
+ end
30
+
31
+ class ObjectRecords
32
+ def self.generate(report,*options)
33
+ records = []
34
+ options = options.shift || {}
35
+ (0..(options[:count].to_i)).each do |index|
36
+ object = OpenStruct.new(report.columns)
37
+ report.columns.each do |column|
38
+ value = case column.to_s
39
+ when /_at$/
40
+ (DateTime.now-100)+index
41
+ when /_on$/
42
+ (Date.today-100)+index
43
+ when /_id$/,/_count$/
44
+ rand(10000)
45
+ else
46
+ column
47
+ end
48
+
49
+ object.send(column.to_sym, value)
50
+
51
+ records << object
52
+ end
53
+ records
54
+ end
55
+ end
56
+ end
57
+
58
+ class ARRecords
59
+ def self.generate(*options)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
metadata CHANGED
@@ -1,33 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamic_reports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
- - Wayne E. Seguin & Joshua Lippiner
7
+ - Wayne E. Seguin
8
+ - Joshua Lippiner
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2009-06-26 00:00:00 -04:00
13
+ date: 2009-07-01 00:00:00 -04:00
13
14
  default_executable:
14
15
  dependencies: []
15
16
 
16
- description: Dynamic Ruby Reporting Engine with support for Charts
17
+ description: Dynamic Ruby Reporting Engine with support for Charts.
17
18
  email: wayneeseguin@gmail.com, jlippiner@gmail.com
18
19
  executables: []
19
20
 
20
21
  extensions: []
21
22
 
22
- extra_rdoc_files: []
23
-
23
+ extra_rdoc_files:
24
+ - README
25
+ - README.rdoc
24
26
  files:
25
27
  - HISTORY
26
28
  - README
27
- - gemspec.rb
29
+ - dynamic_reports.gemspec
30
+ - lib/dynamic_reports.rb
28
31
  - lib/dynamic_reports/charts.rb
29
32
  - lib/dynamic_reports/reports.rb
30
33
  - lib/dynamic_reports/templates.rb
34
+ - lib/dynamic_reports/vendor/google_chart.rb
31
35
  - lib/dynamic_reports/vendor/google_chart/bar_chart.rb
32
36
  - lib/dynamic_reports/vendor/google_chart/base.rb
33
37
  - lib/dynamic_reports/vendor/google_chart/financial_line_chart.rb
@@ -35,19 +39,19 @@ files:
35
39
  - lib/dynamic_reports/vendor/google_chart/pie_chart.rb
36
40
  - lib/dynamic_reports/vendor/google_chart/scatter_chart.rb
37
41
  - lib/dynamic_reports/vendor/google_chart/venn_diagram.rb
38
- - lib/dynamic_reports/vendor/google_chart.rb
42
+ - lib/dynamic_reports/views.rb
39
43
  - lib/dynamic_reports/views/default_layout.html.erb
40
44
  - lib/dynamic_reports/views/default_report.html.erb
41
45
  - lib/dynamic_reports/views/default_report.html.haml
42
- - lib/dynamic_reports/views.rb
43
- - lib/dynamic_reports.rb
46
+ - README.rdoc
44
47
  has_rdoc: true
45
- homepage: http://github.com/wayneeseguin/direct_reports
48
+ homepage: http://dynamicreports.rubyforge.org/
46
49
  licenses: []
47
50
 
48
51
  post_install_message:
49
- rdoc_options: []
50
-
52
+ rdoc_options:
53
+ - --inline-source
54
+ - --charset=UTF-8
51
55
  require_paths:
52
56
  - lib
53
57
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -64,10 +68,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
68
  version:
65
69
  requirements: []
66
70
 
67
- rubyforge_project: dynamic_reports
68
- rubygems_version: 1.3.3
71
+ rubyforge_project: dynamicreports
72
+ rubygems_version: 1.3.4
69
73
  signing_key:
70
74
  specification_version: 3
71
- summary: dynamic_reports
72
- test_files: []
73
-
75
+ summary: Dynamic Ruby Reporting Engine with support for Charts
76
+ test_files:
77
+ - test/dynamic_reports/charts_test.rb
78
+ - test/dynamic_reports/reports_test.rb
79
+ - test/dynamic_reports/templates_test.rb
80
+ - test/dynamic_reports/views_test.rb
81
+ - test/dynamic_reports.rb
82
+ - test/factories/records.rb
83
+ - test/test_helper.rb
data/gemspec.rb DELETED
@@ -1,24 +0,0 @@
1
- require "rubygems"
2
-
3
- library="dynamic_reports"
4
- version="0.0.1"
5
-
6
- Gem::Specification::new do |spec|
7
- $VERBOSE = nil
8
- spec.name = library
9
- spec.summary = library
10
- spec.version = version
11
- spec.description = "Dynamic Ruby Reporting Engine with support for Charts"
12
- spec.platform = Gem::Platform::RUBY
13
- spec.files = ["HISTORY", "README", "gemspec.rb", Dir::glob("lib/**/**")].flatten
14
- spec.executables = Dir::glob("bin/*").map{ |script| File::basename script }
15
- spec.require_path = "lib"
16
- spec.has_rdoc = File::exist?("doc")
17
- spec.author = "Wayne E. Seguin & Joshua Lippiner"
18
- spec.email = "wayneeseguin@gmail.com, jlippiner@gmail.com"
19
- spec.homepage = "http://github.com/wayneeseguin/direct_reports"
20
- # spec.test_suite_file = "test/#{library}.rb" if File::directory?("test")
21
- #spec.add_dependency "", ">= 0.0"
22
- spec.extensions << "extconf.rb" if File::exists?("extconf.rb")
23
- spec.rubyforge_project = library
24
- end