ruby_reportable 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gemtest +0 -0
- data/.rspec +2 -0
- data/.rvmrc +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +20 -0
- data/README.md +47 -0
- data/Rakefile +19 -0
- data/design +34 -0
- data/examples/object_space_report.rb +16 -0
- data/lib/ruby_reportable.rb +19 -0
- data/lib/ruby_reportable/base.rb +15 -0
- data/lib/ruby_reportable/filter.rb +47 -0
- data/lib/ruby_reportable/output.rb +19 -0
- data/lib/ruby_reportable/report.rb +202 -0
- data/lib/ruby_reportable/sandbox.rb +52 -0
- data/lib/ruby_reportable/source.rb +23 -0
- data/lib/ruby_reportable/version.rb +3 -0
- data/ruby_reportable.gemspec +24 -0
- data/spec/base_spec.rb +6 -0
- data/spec/filter_spec.rb +30 -0
- data/spec/report_spec.rb +158 -0
- data/spec/sandbox_spec.rb +41 -0
- data/spec/source_spec.rb +20 -0
- data/spec/spec_helper.rb +10 -0
- metadata +109 -0
data/.gemtest
ADDED
File without changes
|
data/.rspec
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby_reportable (0.0.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
rr (1.0.4)
|
11
|
+
rspec (2.10.0)
|
12
|
+
rspec-core (~> 2.10.0)
|
13
|
+
rspec-expectations (~> 2.10.0)
|
14
|
+
rspec-mocks (~> 2.10.0)
|
15
|
+
rspec-core (2.10.0)
|
16
|
+
rspec-expectations (2.10.0)
|
17
|
+
diff-lcs (~> 1.1.3)
|
18
|
+
rspec-mocks (2.10.1)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
rr
|
25
|
+
rspec
|
26
|
+
ruby_reportable!
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 John "asceth" Long
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Ruby Reportable - reporting in ruby
|
2
|
+
===================================
|
3
|
+
|
4
|
+
|
5
|
+
## DESCRIPTION
|
6
|
+
|
7
|
+
Ruby Reportable is a DSL for writing reports using pure ruby. It allows you to use
|
8
|
+
existing code (ie your Rails application) to grab data and the manipulate it.
|
9
|
+
|
10
|
+
If you have loaded it then Ruby Reportable can report on it.
|
11
|
+
|
12
|
+
|
13
|
+
## Examples
|
14
|
+
|
15
|
+
See examples/ directory
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Using Ruby Reportable is as easy as ```include RubyReportable``` in a new class. The include lets Ruby Reportable know this class is a new report and also brings in the DSL.
|
20
|
+
|
21
|
+
### Source
|
22
|
+
|
23
|
+
Your source is the start of your data. All available configuration is shown below.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
source do
|
27
|
+
#
|
28
|
+
# define how your outputs will see each element of the source data
|
29
|
+
#
|
30
|
+
as :element # this is the default
|
31
|
+
|
32
|
+
# Whatever you want your starting data to be
|
33
|
+
logic do
|
34
|
+
ObjectSpace.each_object.to_a.group_by(&:class).to_a
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
## INSTALLATION
|
40
|
+
|
41
|
+
Install as a gem or use as part of your Gemfile
|
42
|
+
|
43
|
+
$ [sudo] gem install ruby_reportable
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require "rspec"
|
5
|
+
require "rspec/core/rake_task"
|
6
|
+
|
7
|
+
Rspec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
gemspec = eval(File.read(File.join(Dir.pwd, "ruby_reportable.gemspec")))
|
10
|
+
|
11
|
+
task :build => "#{gemspec.full_name}.gem"
|
12
|
+
|
13
|
+
task :test => :spec
|
14
|
+
|
15
|
+
file "#{gemspec.full_name}.gem" => gemspec.files + ["ruby_reportable.gemspec"] do
|
16
|
+
system "gem build ruby_reportable.gemspec"
|
17
|
+
system "gem install ruby_reportable-#{RubyReportable::VERSION}.gem"
|
18
|
+
end
|
19
|
+
|
data/design
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
RubyReportable.define :test do
|
2
|
+
source do
|
3
|
+
as :client_application
|
4
|
+
|
5
|
+
logic do
|
6
|
+
ClientApplication.joins(:assigned_worker, :client)
|
7
|
+
end
|
8
|
+
|
9
|
+
filter(:unless => proc { meta[:current_user].roles.include?(:all) }) do
|
10
|
+
source.where('client_applications.assigned_worker_id' => meta[:current_user].id)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
output('CLTC #') { client_application.client.cltc_number }
|
15
|
+
output('Program') { client_application.program.to_s }
|
16
|
+
output('Worker') { client_application.assigned_worker.to_s }
|
17
|
+
output('Level of Care') { client_application.latest_assessment.care_level.to_s }
|
18
|
+
|
19
|
+
|
20
|
+
filter('Program') do
|
21
|
+
require
|
22
|
+
key :program
|
23
|
+
on :data
|
24
|
+
input(:multiple) { Program.all.map(&:to_s) }
|
25
|
+
|
26
|
+
logic do
|
27
|
+
client_application.program.to_s == input
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
RubyReportable.reports[:test].run(:meta => {:current_user => nil}, :input => {:program => 'Community Choices'})
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class ObjectSpaceReport
|
2
|
+
include RubyReportable
|
3
|
+
|
4
|
+
name 'Object Space By Class'
|
5
|
+
|
6
|
+
source do
|
7
|
+
as :object
|
8
|
+
|
9
|
+
logic do
|
10
|
+
ObjectSpace.each_object.to_a.group_by(&:class).to_a
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
output('Class') { object.first.to_s }
|
15
|
+
output('Total') { object.last.size }
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
unless Kernel.respond_to?(:require_relative)
|
2
|
+
module Kernel
|
3
|
+
def require_relative(path)
|
4
|
+
require File.join(File.dirname(caller[0]), path.to_str)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative 'ruby_reportable/base'
|
10
|
+
require_relative 'ruby_reportable/filter'
|
11
|
+
require_relative 'ruby_reportable/output'
|
12
|
+
require_relative 'ruby_reportable/sandbox'
|
13
|
+
require_relative 'ruby_reportable/source'
|
14
|
+
|
15
|
+
|
16
|
+
require_relative 'ruby_reportable/report'
|
17
|
+
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RubyReportable
|
2
|
+
class Filter
|
3
|
+
def initialize(name)
|
4
|
+
@options = {}
|
5
|
+
@options[:key] = name.to_s.downcase.gsub(' ', '_').gsub(/[^a-zA-Z_]+/, '')
|
6
|
+
@options[:name] = name
|
7
|
+
@options[:require] = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
@options[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
def []=(key, value)
|
15
|
+
@options[key] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def require
|
19
|
+
self[:require] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def priority(value)
|
23
|
+
self[:priority] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def key(key)
|
27
|
+
self[:key] = key
|
28
|
+
end
|
29
|
+
|
30
|
+
def use?(&block)
|
31
|
+
self[:use] = block
|
32
|
+
end
|
33
|
+
|
34
|
+
def input(type, &block)
|
35
|
+
self[:input] = type
|
36
|
+
self[:collection] = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid?(&block)
|
40
|
+
self[:valid] = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def logic(&block)
|
44
|
+
self[:logic] = block
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RubyReportable
|
2
|
+
class Output
|
3
|
+
attr_accessor :name, :logic
|
4
|
+
|
5
|
+
def initialize(name, options, block)
|
6
|
+
@name = name
|
7
|
+
@options = options
|
8
|
+
@logic = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@options[key]
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(key, value)
|
16
|
+
@options[key] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module RubyReportable
|
2
|
+
module Report
|
3
|
+
attr_accessor :data_source, :outputs, :filters
|
4
|
+
|
5
|
+
def clear
|
6
|
+
@outputs = []
|
7
|
+
@filters = {}
|
8
|
+
@data_source = nil
|
9
|
+
@report = self.to_s
|
10
|
+
@category = 'Reports'
|
11
|
+
@meta = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def meta(key, value = nil, &block)
|
15
|
+
if block_given?
|
16
|
+
@meta[key] = block
|
17
|
+
else
|
18
|
+
if value.nil?
|
19
|
+
@meta[key]
|
20
|
+
else
|
21
|
+
@meta[key] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def report(string = nil)
|
27
|
+
if string.nil?
|
28
|
+
@report
|
29
|
+
else
|
30
|
+
@report = string
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def category(string = nil)
|
35
|
+
if string.nil?
|
36
|
+
@category
|
37
|
+
else
|
38
|
+
@category = string
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def source(&block)
|
43
|
+
@data_source = RubyReportable::Source.new
|
44
|
+
@data_source.instance_eval(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def filter(name, &block)
|
48
|
+
@filters[name] = RubyReportable::Filter.new(name)
|
49
|
+
@filters[name].instance_eval(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def finalize(&block)
|
53
|
+
@finalize = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def output(name, options = {}, &block)
|
57
|
+
@outputs << RubyReportable::Output.new(name, options, block)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# methods you shouldn't use inside the blocks
|
63
|
+
#
|
64
|
+
def useable_filters(scope)
|
65
|
+
@filters.values.select {|filter| !filter[:input].nil? && (filter[:use].nil? || filter[:use].call(scope))}.sort_by {|filter| filter[:priority].to_i}
|
66
|
+
end
|
67
|
+
|
68
|
+
def _filter(filters, original_sandbox, options)
|
69
|
+
# sort filters by priority then apply to sandbox
|
70
|
+
filters.sort_by do |filter_name, filter|
|
71
|
+
filter[:priority]
|
72
|
+
end.inject(original_sandbox) do |sandbox, (filter_name, filter)|
|
73
|
+
|
74
|
+
# find input for given filter
|
75
|
+
sandbox[:input] = options[:input][filter[:key]] if options[:input].is_a?(Hash)
|
76
|
+
|
77
|
+
if filter[:valid].nil? || sandbox.instance_eval(&filter[:valid])
|
78
|
+
if filter[:logic].nil?
|
79
|
+
sandbox
|
80
|
+
else
|
81
|
+
sandbox.build(:source, filter[:logic])
|
82
|
+
end
|
83
|
+
else
|
84
|
+
sandbox
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def _source(options = {})
|
90
|
+
# build sandbox for getting the data
|
91
|
+
RubyReportable::Sandbox.new(:meta => @meta, :source => @data_source[:logic], :inputs => options[:input] || {}, :input => nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
def _data(sandbox, options = {})
|
95
|
+
_filter(@filters, sandbox, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
def _finalize(sandbox, options = {})
|
99
|
+
if @finalize.nil?
|
100
|
+
sandbox
|
101
|
+
else
|
102
|
+
sandbox[:inputs] = options[:input] || {}
|
103
|
+
sandbox.build(:source, @finalize)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def _output(source_data, options = {})
|
108
|
+
# build sandbox for building outputs
|
109
|
+
sandbox = RubyReportable::Sandbox.new(:meta => @meta, @data_source[:as] => nil)
|
110
|
+
|
111
|
+
source_data.inject({:results => []}) do |rows, element|
|
112
|
+
# fill sandbox with data element
|
113
|
+
sandbox[@data_source[:as]] = element
|
114
|
+
|
115
|
+
# grab outputs
|
116
|
+
rows[:results] << @outputs.inject({}) do |row, output|
|
117
|
+
row[output.name] = sandbox.instance_eval(&output.logic)
|
118
|
+
row
|
119
|
+
end
|
120
|
+
|
121
|
+
rows
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def _group(group, data, options = {})
|
126
|
+
unless group.to_s.empty?
|
127
|
+
data[:results].inject({}) do |hash, element|
|
128
|
+
key = element[group]
|
129
|
+
hash[key] ||= []
|
130
|
+
hash[key] << element
|
131
|
+
hash
|
132
|
+
end
|
133
|
+
else
|
134
|
+
data
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def _sort(sort, data, options = {})
|
139
|
+
unless sort.to_s.empty?
|
140
|
+
data.inject(Hash.new([])) do |hash, (group, elements)|
|
141
|
+
hash[group] = elements.sort_by {|element| element[sort]}
|
142
|
+
hash
|
143
|
+
end
|
144
|
+
else
|
145
|
+
data
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def run(options = {})
|
150
|
+
options = {:input => {}}.merge(options)
|
151
|
+
|
152
|
+
# initial sandbox
|
153
|
+
sandbox = _source(options)
|
154
|
+
|
155
|
+
# apply filters to source
|
156
|
+
filtered_sandbox = _data(sandbox, options)
|
157
|
+
|
158
|
+
# finalize raw data from source
|
159
|
+
source_data = _finalize(filtered_sandbox, options).source
|
160
|
+
|
161
|
+
# {:default => [{outputs => values}]
|
162
|
+
data = _output(source_data, options)
|
163
|
+
|
164
|
+
# transform into {group => [outputs => values]}
|
165
|
+
grouped = _group(options[:group], data, options)
|
166
|
+
|
167
|
+
# sort grouped data
|
168
|
+
_sort(options[:sort], grouped, options)
|
169
|
+
end # end def run
|
170
|
+
|
171
|
+
def valid?(options = {})
|
172
|
+
options = {:input => {}}.merge(options)
|
173
|
+
|
174
|
+
# initial sandbox
|
175
|
+
sandbox = _source(options)
|
176
|
+
|
177
|
+
# add in inputs
|
178
|
+
sandbox[:inputs] = options[:input]
|
179
|
+
|
180
|
+
validity = @filters.map do |filter_name, filter|
|
181
|
+
# find input for given filter
|
182
|
+
sandbox[:input] = options[:input][filter[:key]] if options[:input].is_a?(Hash)
|
183
|
+
|
184
|
+
filter_validity = filter[:valid].nil? || sandbox.instance_eval(&filter[:valid])
|
185
|
+
|
186
|
+
if filter_validity == false
|
187
|
+
# filter failed validity test, if it's a required filter
|
188
|
+
# we return false, otherwise its optional so return true
|
189
|
+
if filter[:require]
|
190
|
+
false
|
191
|
+
else
|
192
|
+
true
|
193
|
+
end
|
194
|
+
else
|
195
|
+
true
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
!validity.include?(false)
|
200
|
+
end # end def valid?
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RubyReportable
|
2
|
+
class Sandbox
|
3
|
+
def metaclass
|
4
|
+
class << self; self; end
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(_methods = {})
|
8
|
+
@values = {}
|
9
|
+
|
10
|
+
_methods.map do |key, value|
|
11
|
+
define(key, value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(base, block)
|
16
|
+
@values[base] = instance_eval(&block)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](key)
|
21
|
+
@values[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(key, value)
|
25
|
+
if value.is_a?(Proc)
|
26
|
+
@values[key] = value.call
|
27
|
+
else
|
28
|
+
@values[key] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def define(key, value)
|
33
|
+
if self.class.respond_to?(:define_singleton_method)
|
34
|
+
define_singleton_method(key) do
|
35
|
+
if value.is_a?(Proc)
|
36
|
+
@values[key] ||= value.call
|
37
|
+
else
|
38
|
+
@values[key] ||= value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
metaclass.send(:define_method, key, Proc.new do
|
43
|
+
if value.is_a?(Proc)
|
44
|
+
@values[key] ||= value.call
|
45
|
+
else
|
46
|
+
@values[key] ||= value
|
47
|
+
end
|
48
|
+
end)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RubyReportable
|
2
|
+
class Source
|
3
|
+
def initialize
|
4
|
+
@options = {:filters => [], :as => :element}
|
5
|
+
end
|
6
|
+
|
7
|
+
def [](key)
|
8
|
+
@options[key]
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
@options[key] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def as(variable_name)
|
16
|
+
self[:as] = variable_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def logic(&block)
|
20
|
+
self[:logic] = block
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "ruby_reportable/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "ruby_reportable"
|
6
|
+
s.version = RubyReportable::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["John 'asceth' Long"]
|
9
|
+
s.email = ["machinist@asceth.com"]
|
10
|
+
s.homepage = "http://github.com/asceth/ruby_reportable"
|
11
|
+
s.summary = "Ruby Reporting"
|
12
|
+
s.description = "Allows you to write reports that use existing ruby classes/methods to present/filter the data"
|
13
|
+
|
14
|
+
s.rubyforge_project = "ruby_reportable"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency 'rspec'
|
22
|
+
s.add_development_dependency 'rr'
|
23
|
+
end
|
24
|
+
|
data/spec/base_spec.rb
ADDED
data/spec/filter_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RubyReportable::Filter do
|
4
|
+
|
5
|
+
context "a filter" do
|
6
|
+
before do
|
7
|
+
@filter = RubyReportable::Filter.new('G Methods')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should store variables internally" do
|
11
|
+
@filter[:logic].should == nil
|
12
|
+
|
13
|
+
@filter.key(:g)
|
14
|
+
@filter.logic do
|
15
|
+
element.include?('g')
|
16
|
+
end
|
17
|
+
|
18
|
+
@filter[:key].should == :g
|
19
|
+
@filter[:logic].should_not == nil
|
20
|
+
end
|
21
|
+
|
22
|
+
context "#require" do
|
23
|
+
it "should automatically assume it is required when no block is given" do
|
24
|
+
@filter.require
|
25
|
+
|
26
|
+
@filter[:require].should == true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/report_spec.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class FooReport; include RubyReportable; end
|
4
|
+
|
5
|
+
describe RubyReportable::Report do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@report = FooReport
|
9
|
+
@report.clear
|
10
|
+
end
|
11
|
+
|
12
|
+
context "#_source" do
|
13
|
+
before do
|
14
|
+
@report.source do
|
15
|
+
logic do
|
16
|
+
Object.methods
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should contruct a sandbox to build up source data" do
|
22
|
+
sandbox = @report._source
|
23
|
+
|
24
|
+
sandbox.source.should == Object.methods
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "#_data" do
|
29
|
+
before do
|
30
|
+
@report.source do
|
31
|
+
logic do
|
32
|
+
Object.methods
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be able to filter on source data" do
|
38
|
+
@report.filter('G Methods') do
|
39
|
+
logic do
|
40
|
+
source.select {|element| element.to_s.include?('g')}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@report._data(@report._source).source.should == Object.methods.select {|method| method.to_s.include?('g')}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should use available input when filtering data" do
|
48
|
+
@report.filter('Filter Methods') do
|
49
|
+
key :letter
|
50
|
+
|
51
|
+
logic do
|
52
|
+
source.select {|element| element.to_s.include?(input)}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
source_sandbox = @report._source
|
57
|
+
options = {:input => {:letter => 'g'}}
|
58
|
+
@report._data(source_sandbox, options).source.should == Object.methods.select {|method| method.to_s.include?('g')}
|
59
|
+
|
60
|
+
source_sandbox = @report._source
|
61
|
+
options = {:input => {:letter => 'r'}}
|
62
|
+
@report._data(source_sandbox, options).source.should == Object.methods.select {|method| method.to_s.include?('r')}
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should handle multiple filters" do
|
66
|
+
@report.filter('G Methods') do
|
67
|
+
priority 0
|
68
|
+
|
69
|
+
logic do
|
70
|
+
source.select {|element| element.to_s.include?('g')}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@report.filter('R Methods') do
|
75
|
+
priority 1
|
76
|
+
|
77
|
+
logic do
|
78
|
+
source.select {|element| element.to_s.include?('r')}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
source_sandbox = @report._source
|
83
|
+
|
84
|
+
@report._data(source_sandbox).source.should == Object.methods.select {|method| method.to_s.include?('g') && method.to_s.include?('r')}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should handle multiple filters in the right priority" do
|
88
|
+
@report.source do
|
89
|
+
logic do
|
90
|
+
Object.methods
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
@report.filter('G Methods') do
|
95
|
+
priority 0
|
96
|
+
|
97
|
+
logic do
|
98
|
+
source.map(&:to_s).select {|element| element.include?('g')}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
@report.filter('R Methods') do
|
103
|
+
priority 1
|
104
|
+
|
105
|
+
logic do
|
106
|
+
source.select {|element| element.include?('r')}.map(&:to_sym)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
source_sandbox = @report._source
|
111
|
+
|
112
|
+
# G Methods has to run first because otherwise Object.methods
|
113
|
+
# returns symbols and R Methods is trying to do a include? on a symbol
|
114
|
+
@report._data(source_sandbox).source.should == Object.methods.select {|method| method.to_s.include?('g') && method.to_s.include?('r')}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "#_group" do
|
119
|
+
it "should group results" do
|
120
|
+
@report.source do
|
121
|
+
logic do
|
122
|
+
[:method]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
@report.output('name') do
|
127
|
+
element
|
128
|
+
end
|
129
|
+
|
130
|
+
@report.output('letter') do
|
131
|
+
element.to_s[0]
|
132
|
+
end
|
133
|
+
|
134
|
+
source_data = @report._data(@report._source).source
|
135
|
+
|
136
|
+
@report._group('letter', @report._output(source_data)).should == {"m" => [{'name' => :method, 'letter' => 'm'}]}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "#_sort" do
|
141
|
+
it "should sort results" do
|
142
|
+
@report.source do
|
143
|
+
logic do
|
144
|
+
Object.methods
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
@report.output('name') do
|
149
|
+
element
|
150
|
+
end
|
151
|
+
|
152
|
+
source_data = @report._data(@report._source).source
|
153
|
+
final = @report._sort('name', @report._output(source_data))
|
154
|
+
|
155
|
+
final.should == {:results => Object.methods.sort.map {|method| {'name' => method}}}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RubyReportable::Sandbox do
|
4
|
+
|
5
|
+
context "a sandbox" do
|
6
|
+
before do
|
7
|
+
@sandbox = RubyReportable::Sandbox.new(:source => [1, 2])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should create new methods based on options" do
|
11
|
+
RubyReportable::Sandbox.new.respond_to?(:source).should == false
|
12
|
+
@sandbox.respond_to?(:source).should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should store the value of a defined call" do
|
16
|
+
@sandbox.source.should == [1, 2]
|
17
|
+
@sandbox[:source].should == [1, 2]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not call a method when trying to retrieve the stored value directly" do
|
21
|
+
@sandbox[:source].should == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to define a new method with value dynamically" do
|
25
|
+
@sandbox.define(:arbitrary, proc { Time.now })
|
26
|
+
@sandbox.respond_to?(:arbitrary).should == true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not override a stored value with a method call" do
|
30
|
+
@sandbox[:source] = [3, 4]
|
31
|
+
@sandbox.source.should == [3, 4]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should allow you to build up a stored value" do
|
35
|
+
@sandbox[:source] = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
36
|
+
@sandbox.build(:source, proc { source.select(&:odd?) })
|
37
|
+
|
38
|
+
@sandbox[:source].should == [1, 3, 5, 7, 9]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/source_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RubyReportable::Source do
|
4
|
+
|
5
|
+
context "a source" do
|
6
|
+
before do
|
7
|
+
@source = RubyReportable::Source.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should store as and logic variables" do
|
11
|
+
@source.as(:element)
|
12
|
+
@source.logic do
|
13
|
+
Object.methods.sort
|
14
|
+
end
|
15
|
+
|
16
|
+
@source[:as].should == :element
|
17
|
+
@source[:logic].call.should == Object.methods.sort
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_reportable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John 'asceth' Long
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rr
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Allows you to write reports that use existing ruby classes/methods to
|
47
|
+
present/filter the data
|
48
|
+
email:
|
49
|
+
- machinist@asceth.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- .gemtest
|
55
|
+
- .rspec
|
56
|
+
- .rvmrc
|
57
|
+
- Gemfile
|
58
|
+
- Gemfile.lock
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- design
|
63
|
+
- examples/object_space_report.rb
|
64
|
+
- lib/ruby_reportable.rb
|
65
|
+
- lib/ruby_reportable/base.rb
|
66
|
+
- lib/ruby_reportable/filter.rb
|
67
|
+
- lib/ruby_reportable/output.rb
|
68
|
+
- lib/ruby_reportable/report.rb
|
69
|
+
- lib/ruby_reportable/sandbox.rb
|
70
|
+
- lib/ruby_reportable/source.rb
|
71
|
+
- lib/ruby_reportable/version.rb
|
72
|
+
- ruby_reportable.gemspec
|
73
|
+
- spec/base_spec.rb
|
74
|
+
- spec/filter_spec.rb
|
75
|
+
- spec/report_spec.rb
|
76
|
+
- spec/sandbox_spec.rb
|
77
|
+
- spec/source_spec.rb
|
78
|
+
- spec/spec_helper.rb
|
79
|
+
homepage: http://github.com/asceth/ruby_reportable
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project: ruby_reportable
|
99
|
+
rubygems_version: 1.8.24
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: Ruby Reporting
|
103
|
+
test_files:
|
104
|
+
- spec/base_spec.rb
|
105
|
+
- spec/filter_spec.rb
|
106
|
+
- spec/report_spec.rb
|
107
|
+
- spec/sandbox_spec.rb
|
108
|
+
- spec/source_spec.rb
|
109
|
+
- spec/spec_helper.rb
|