dynamic_reports 0.0.1 → 0.0.4

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/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