activewarehouse 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +41 -0
- data/Rakefile +121 -0
- data/TODO +4 -0
- data/db/migrations/001_create_table_reports.rb +28 -0
- data/doc/agg_queries.txt +26 -0
- data/doc/agg_queries_results.txt +150 -0
- data/doc/queries.txt +35 -0
- data/generators/cube/USAGE +1 -0
- data/generators/cube/cube_generator.rb +28 -0
- data/generators/cube/templates/model.rb +3 -0
- data/generators/cube/templates/unit_test.rb +8 -0
- data/generators/dimension/USAGE +1 -0
- data/generators/dimension/dimension_generator.rb +46 -0
- data/generators/dimension/templates/fixture.yml +5 -0
- data/generators/dimension/templates/migration.rb +20 -0
- data/generators/dimension/templates/model.rb +3 -0
- data/generators/dimension/templates/unit_test.rb +10 -0
- data/generators/fact/USAGE +1 -0
- data/generators/fact/fact_generator.rb +46 -0
- data/generators/fact/templates/fixture.yml +5 -0
- data/generators/fact/templates/migration.rb +11 -0
- data/generators/fact/templates/model.rb +3 -0
- data/generators/fact/templates/unit_test.rb +10 -0
- data/install.rb +5 -0
- data/lib/active_warehouse.rb +65 -0
- data/lib/active_warehouse/builder.rb +2 -0
- data/lib/active_warehouse/builder/date_dimension_builder.rb +65 -0
- data/lib/active_warehouse/builder/random_data_builder.rb +13 -0
- data/lib/active_warehouse/core_ext.rb +1 -0
- data/lib/active_warehouse/core_ext/time.rb +5 -0
- data/lib/active_warehouse/core_ext/time/calculations.rb +40 -0
- data/lib/active_warehouse/migrations.rb +65 -0
- data/lib/active_warehouse/model.rb +5 -0
- data/lib/active_warehouse/model/aggregate.rb +244 -0
- data/lib/active_warehouse/model/cube.rb +273 -0
- data/lib/active_warehouse/model/dimension.rb +3 -0
- data/lib/active_warehouse/model/dimension/bridge.rb +32 -0
- data/lib/active_warehouse/model/dimension/dimension.rb +152 -0
- data/lib/active_warehouse/model/dimension/hierarchical_dimension.rb +35 -0
- data/lib/active_warehouse/model/fact.rb +96 -0
- data/lib/active_warehouse/model/report.rb +3 -0
- data/lib/active_warehouse/model/report/abstract_report.rb +121 -0
- data/lib/active_warehouse/model/report/chart_report.rb +9 -0
- data/lib/active_warehouse/model/report/table_report.rb +23 -0
- data/lib/active_warehouse/version.rb +9 -0
- data/lib/active_warehouse/view.rb +2 -0
- data/lib/active_warehouse/view/report_helper.rb +213 -0
- data/tasks/active_warehouse_tasks.rake +50 -0
- metadata +144 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveWarehouse
|
2
|
+
module Report
|
3
|
+
# A report which is used to represent a tabular report.
|
4
|
+
class TableReport < ActiveRecord::Base
|
5
|
+
include AbstractReport
|
6
|
+
before_save :to_storage
|
7
|
+
after_save :from_storage
|
8
|
+
attr_accessor :format, :link_cell, :html_params
|
9
|
+
|
10
|
+
def format
|
11
|
+
@format ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def link_cell
|
15
|
+
@link_cell ||= false
|
16
|
+
end
|
17
|
+
|
18
|
+
def html_params
|
19
|
+
@html_params ||= {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module ReportHelper
|
2
|
+
|
3
|
+
# Render the report.
|
4
|
+
# * <tt>report</tt>: The report instance
|
5
|
+
# * <tt>html_options</tt>: HTML options
|
6
|
+
def render_report(report, html_options={})
|
7
|
+
case report
|
8
|
+
when ActiveWarehouse::Report::TableReport
|
9
|
+
render_table_report(report, html_options)
|
10
|
+
when ActiveWarehouse::Report::ChartReport
|
11
|
+
render_chart_report(report, html_options)
|
12
|
+
else
|
13
|
+
raise "Unsupported report type: #{report.class}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def render_table_report(report, html_options)
|
19
|
+
cube = report.cube
|
20
|
+
fact_class = report.fact_class
|
21
|
+
column_dimension_class = report.column_dimension_class
|
22
|
+
row_dimension_class = report.row_dimension_class
|
23
|
+
|
24
|
+
column_dimension = report.column_dimension_name
|
25
|
+
row_dimension = report.row_dimension_name
|
26
|
+
|
27
|
+
column_hierarchy = report.column_hierarchy
|
28
|
+
column_hierarchy_constraints = report.column_constraints
|
29
|
+
|
30
|
+
row_hierarchy = report.row_hierarchy
|
31
|
+
row_hierarchy_constraints = report.row_constraints
|
32
|
+
|
33
|
+
fact_attributes = report.fact_attributes
|
34
|
+
|
35
|
+
html_params = report.html_params
|
36
|
+
|
37
|
+
cstage = (params[:cstage] || report.column_stage).to_i
|
38
|
+
rstage = (params[:rstage] || report.row_stage).to_i
|
39
|
+
|
40
|
+
col_param_prefix = report.column_param_prefix
|
41
|
+
row_param_prefix = report.row_param_prefix
|
42
|
+
|
43
|
+
link_cell = report.link_cell
|
44
|
+
format = report.format
|
45
|
+
|
46
|
+
# Get the dimension instances
|
47
|
+
col_dims = find_column_dimensions(column_dimension_class, column_hierarchy, cstage, col_param_prefix)
|
48
|
+
row_dims = find_row_dimensions(row_dimension_class, row_hierarchy, rstage, row_param_prefix)
|
49
|
+
|
50
|
+
# Get the aggregate map
|
51
|
+
agg_map = cube.aggregate_map(column_dimension, column_hierarchy, row_dimension, row_hierarchy, cstage, rstage)
|
52
|
+
|
53
|
+
table_attributes = {}
|
54
|
+
table_attributes[:class] = html_options[:report_class] ||= 'report'
|
55
|
+
|
56
|
+
col_hierarchy_length = column_dimension_class.hierarchy(column_hierarchy).length
|
57
|
+
row_hierarchy_length = row_dimension_class.hierarchy(row_hierarchy).length
|
58
|
+
col_group = column_dimension_class.hierarchy(column_hierarchy)[cstage]
|
59
|
+
row_group = row_dimension_class.hierarchy(row_hierarchy)[rstage]
|
60
|
+
|
61
|
+
# build the paths back to the top of the two dimension hierarchies
|
62
|
+
col_path = []
|
63
|
+
row_path = []
|
64
|
+
0.upto(cstage) do |s|
|
65
|
+
p = params["#{col_param_prefix}_#{column_dimension_class.hierarchy(column_hierarchy)[s]}"]
|
66
|
+
col_path << p unless p.nil?
|
67
|
+
end
|
68
|
+
0.upto(rstage) do |s|
|
69
|
+
p = params["#{row_param_prefix}_#{row_dimension_class.hierarchy(row_hierarchy)[s]}"]
|
70
|
+
row_path << p unless p.nil?
|
71
|
+
end
|
72
|
+
#col_path << col_group.to_s
|
73
|
+
#row_path << row_group.to_s
|
74
|
+
|
75
|
+
# build the XHTML
|
76
|
+
x = Builder::XmlMarkup.new(:indent => 2)
|
77
|
+
x.table(table_attributes) do |x|
|
78
|
+
x.tr do |x| # column dimension
|
79
|
+
x.th
|
80
|
+
col_dims.each do |col_dim|
|
81
|
+
col_dim_value = col_dim.send(col_group)
|
82
|
+
x.th({:colspan => fact_attributes.length}) do |x|
|
83
|
+
x << link_to_if((cstage < col_hierarchy_length - 1), col_dim_value, {
|
84
|
+
:cstage => cstage + 1,
|
85
|
+
:rstage => rstage,
|
86
|
+
"#{col_param_prefix}_#{col_group}" => col_dim_value}.merge(cube_params).merge(html_params))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
x.tr do |x| # aggregated fact headers
|
92
|
+
x.th
|
93
|
+
col_dims.each do |col_dim|
|
94
|
+
#fact_class.aggregate_fields.each do |field_name, options|
|
95
|
+
# x.th(options[:label].to_s || field_name.to_s.humanize)
|
96
|
+
fact_attributes.each do |fact_attribute|
|
97
|
+
x.th(fact_attribute.to_s.humanize.titleize) # TODO replace with sortable column header
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
row_dims.each do |row_dim| # rows
|
103
|
+
row_dim_value = row_dim.send(row_group)
|
104
|
+
cell_row_path = row_path + [row_dim_value]
|
105
|
+
x.tr do |x|
|
106
|
+
x.td do |x| # row dimension label
|
107
|
+
x << link_to_if((rstage < row_hierarchy_length - 1), row_dim_value, {
|
108
|
+
:cstage => cstage,
|
109
|
+
:rstage => rstage + 1,
|
110
|
+
"#{row_param_prefix}_#{row_group}" => row_dim_value}.merge(cube_params).merge(html_params))
|
111
|
+
end
|
112
|
+
|
113
|
+
col_dims.each do |col_dim| # aggregated facts
|
114
|
+
col_dim_value = col_dim.send(col_group)
|
115
|
+
cell_col_path = col_path + [col_dim_value]
|
116
|
+
fact_attributes.each_with_index do |fact_attribute, index|
|
117
|
+
x.td do |x|
|
118
|
+
#puts "Row path (length=#{cell_row_path.length}): #{cell_row_path.join(':')}"
|
119
|
+
#puts "Col path (length=#{cell_col_path.length}): #{cell_col_path.join(':')}"
|
120
|
+
|
121
|
+
block = format[fact_attribute.to_sym]
|
122
|
+
value = agg_map.value(cell_row_path.join(':'), cell_col_path.join(':'), index).to_s
|
123
|
+
value = block.call(value) if block
|
124
|
+
|
125
|
+
x << link_to_if((cstage < col_hierarchy_length - 1 and rstage < row_hierarchy_length - 1 and link_cell),
|
126
|
+
#agg_map[row_path.join(':')][col_path.join(':')][index].to_s, {
|
127
|
+
value, {
|
128
|
+
:cstage => cstage + 1,
|
129
|
+
:rstage => rstage + 1,
|
130
|
+
"#{col_param_prefix}_#{col_group}" => col_dim_value,
|
131
|
+
"#{row_param_prefix}_#{row_group}" => row_dim_value}.merge(cube_params).merge(html_params))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def render_chart_report(report, html_options)
|
142
|
+
"Not Yet Implemented"
|
143
|
+
end
|
144
|
+
|
145
|
+
protected
|
146
|
+
def cube_params
|
147
|
+
p = {}
|
148
|
+
params.each { |key, value| p[key] = value if key =~ /^r_/ or key =~ /^c_/ }
|
149
|
+
p
|
150
|
+
end
|
151
|
+
def find_column_dimensions(dimension_class, hierarchy, current_stage, param_prefix='c')
|
152
|
+
find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_row_dimensions(dimension_class, hierarchy, current_stage, param_prefix='r')
|
156
|
+
find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
|
160
|
+
# Construct the column find options
|
161
|
+
find_options = {}
|
162
|
+
group_by = []
|
163
|
+
conditions_sql = []
|
164
|
+
conditions_args = []
|
165
|
+
current_stage.to_i.downto(0) do |stage|
|
166
|
+
level = dimension_class.hierarchy(hierarchy)[stage]
|
167
|
+
group_by << level
|
168
|
+
unless stage == current_stage
|
169
|
+
conditions_sql << "#{level} = ?"
|
170
|
+
conditions_args << params["#{param_prefix}_#{level}"]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
find_options[:conditions] = nil
|
174
|
+
if conditions_args.length > 0
|
175
|
+
find_options[:conditions] = [conditions_sql.join(" and ")] + conditions_args
|
176
|
+
end
|
177
|
+
find_options[:group] = find_options[:order] = group_by.join(',')
|
178
|
+
dimension_class.find(:all, find_options)
|
179
|
+
end
|
180
|
+
|
181
|
+
public
|
182
|
+
# Create a sortable column header.
|
183
|
+
#
|
184
|
+
# Parameters are
|
185
|
+
# column_name: The column display name
|
186
|
+
# order_by: The column name from the table used for sorting
|
187
|
+
# link_parameters: Parameters passed to the hypertext link
|
188
|
+
# attributes: Parameters passed to the <th> element
|
189
|
+
#
|
190
|
+
# Example:
|
191
|
+
# sortable_column_header('SSN','ssn')
|
192
|
+
# sortable_column_header('Name','name',{:onclick => 'doSomething();'}, {:title => 'Some Title'})
|
193
|
+
def sortable_column_header(column_name, order_by, link_parameters={}, attributes={})
|
194
|
+
order = 'desc'
|
195
|
+
if params[:order_by].to_s == order_by.to_s
|
196
|
+
sorted = 'sorted'
|
197
|
+
if params[:order] == 'desc' || params[:order].nil?
|
198
|
+
order_css_class = 'order_down'
|
199
|
+
order = 'asc'
|
200
|
+
elsif params[:order] == 'asc'
|
201
|
+
order_css_class = 'order_up'
|
202
|
+
order = 'desc'
|
203
|
+
end
|
204
|
+
else
|
205
|
+
sorted = ''
|
206
|
+
end
|
207
|
+
|
208
|
+
attributes.merge!({:class => "sortable #{sorted} #{order_css_class}"})
|
209
|
+
x.th(attributes) do |th|
|
210
|
+
th << link_to(column_name, params.merge({:order_by => order_by, :order => order}), link_parameters)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
namespace :warehouse do
|
2
|
+
desc "Build a 'standard' date dimension"
|
3
|
+
task :build_date_dimension => :environment do
|
4
|
+
abcs = ActiveRecord::Base.configurations
|
5
|
+
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
6
|
+
|
7
|
+
start_date = (ENV['START_DATE'] ? Time.parse(ENV['START_DATE']) : Time.now.years_ago(5))
|
8
|
+
end_date = (ENV['END_DATE'] ? Time.parse(ENV['END_DATE']) : Time.now )
|
9
|
+
|
10
|
+
ddb = ActiveWarehouse::Builder::DateDimensionBuilder.new(start_date, end_date)
|
11
|
+
ddb.build.each do |record|
|
12
|
+
dd = DateDimension.new
|
13
|
+
record.each do |key,value|
|
14
|
+
dd.send("#{key}=".to_sym, value) if dd.respond_to?(key)
|
15
|
+
end
|
16
|
+
dd.save!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Rebuild the warehouse" # TODO: consider moving this logic somewhere into a class and calling it from here
|
21
|
+
task :rebuild => :environment do
|
22
|
+
puts "Rebuilding data warehouse"
|
23
|
+
# Discover and require all cube models
|
24
|
+
# TODO: do some more research on the potential problems with this
|
25
|
+
models_dir = File.join(File.dirname(__FILE__), '../../../../app/models')
|
26
|
+
Dir.glob(File.join(models_dir, "**", "*.rb")).each do |f|
|
27
|
+
if f =~ /_cube\.rb/
|
28
|
+
require f
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
t = Benchmark.realtime do
|
33
|
+
ActiveWarehouse::Cube.subclasses.each do |subclass|
|
34
|
+
puts "Rebuilding #{subclass}"
|
35
|
+
tc = Benchmark.realtime do
|
36
|
+
subclass.populate(:force => true)
|
37
|
+
end
|
38
|
+
puts "Rebuilt #{subclass} in #{tc}s"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
puts "Data warehouse rebuilt in #{t}s"
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Migrate ActiveWarehouse"
|
45
|
+
task :migrate => :environment do
|
46
|
+
puts "Migrating ActiveWarehouse"
|
47
|
+
migration_directory = File.join(File.dirname(__FILE__), '../db/migrations')
|
48
|
+
ActiveWarehouse::Migrator.migrate(migration_directory, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.0
|
3
|
+
specification_version: 1
|
4
|
+
name: activewarehouse
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-12-19 00:00:00 -05:00
|
8
|
+
summary: Build data warehouses with Rails.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: anthonyeden@gmail.com
|
12
|
+
homepage: http://activewarehouse.rubyforge.org
|
13
|
+
rubyforge_project: activewarehouse
|
14
|
+
description: ActiveWarehouse extends Rails to provide functionality specific for building data warehouses.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Anthony Eden
|
31
|
+
files:
|
32
|
+
- install.rb
|
33
|
+
- README
|
34
|
+
- TODO
|
35
|
+
- Rakefile
|
36
|
+
- db/migrations
|
37
|
+
- db/migrations/001_create_table_reports.rb
|
38
|
+
- doc/agg_queries.txt
|
39
|
+
- doc/agg_queries_results.txt
|
40
|
+
- doc/queries.txt
|
41
|
+
- generators/cube
|
42
|
+
- generators/dimension
|
43
|
+
- generators/fact
|
44
|
+
- generators/cube/cube_generator.rb
|
45
|
+
- generators/cube/templates
|
46
|
+
- generators/cube/USAGE
|
47
|
+
- generators/cube/templates/model.rb
|
48
|
+
- generators/cube/templates/unit_test.rb
|
49
|
+
- generators/dimension/dimension_generator.rb
|
50
|
+
- generators/dimension/templates
|
51
|
+
- generators/dimension/USAGE
|
52
|
+
- generators/dimension/templates/fixture.yml
|
53
|
+
- generators/dimension/templates/migration.rb
|
54
|
+
- generators/dimension/templates/model.rb
|
55
|
+
- generators/dimension/templates/unit_test.rb
|
56
|
+
- generators/fact/fact_generator.rb
|
57
|
+
- generators/fact/templates
|
58
|
+
- generators/fact/USAGE
|
59
|
+
- generators/fact/templates/fixture.yml
|
60
|
+
- generators/fact/templates/migration.rb
|
61
|
+
- generators/fact/templates/model.rb
|
62
|
+
- generators/fact/templates/unit_test.rb
|
63
|
+
- lib/active_warehouse
|
64
|
+
- lib/active_warehouse.rb
|
65
|
+
- lib/active_warehouse/builder
|
66
|
+
- lib/active_warehouse/builder.rb
|
67
|
+
- lib/active_warehouse/core_ext
|
68
|
+
- lib/active_warehouse/core_ext.rb
|
69
|
+
- lib/active_warehouse/migrations.rb
|
70
|
+
- lib/active_warehouse/model
|
71
|
+
- lib/active_warehouse/model.rb
|
72
|
+
- lib/active_warehouse/version.rb
|
73
|
+
- lib/active_warehouse/view
|
74
|
+
- lib/active_warehouse/view.rb
|
75
|
+
- lib/active_warehouse/builder/date_dimension_builder.rb
|
76
|
+
- lib/active_warehouse/builder/random_data_builder.rb
|
77
|
+
- lib/active_warehouse/core_ext/time
|
78
|
+
- lib/active_warehouse/core_ext/time.rb
|
79
|
+
- lib/active_warehouse/core_ext/time/calculations.rb
|
80
|
+
- lib/active_warehouse/model/aggregate.rb
|
81
|
+
- lib/active_warehouse/model/cube.rb
|
82
|
+
- lib/active_warehouse/model/dimension
|
83
|
+
- lib/active_warehouse/model/dimension.rb
|
84
|
+
- lib/active_warehouse/model/fact.rb
|
85
|
+
- lib/active_warehouse/model/report
|
86
|
+
- lib/active_warehouse/model/report.rb
|
87
|
+
- lib/active_warehouse/model/dimension/bridge.rb
|
88
|
+
- lib/active_warehouse/model/dimension/dimension.rb
|
89
|
+
- lib/active_warehouse/model/dimension/hierarchical_dimension.rb
|
90
|
+
- lib/active_warehouse/model/report/abstract_report.rb
|
91
|
+
- lib/active_warehouse/model/report/chart_report.rb
|
92
|
+
- lib/active_warehouse/model/report/table_report.rb
|
93
|
+
- lib/active_warehouse/view/report_helper.rb
|
94
|
+
- tasks/active_warehouse_tasks.rake
|
95
|
+
test_files: []
|
96
|
+
|
97
|
+
rdoc_options:
|
98
|
+
- --exclude
|
99
|
+
- .
|
100
|
+
extra_rdoc_files: []
|
101
|
+
|
102
|
+
executables: []
|
103
|
+
|
104
|
+
extensions: []
|
105
|
+
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
dependencies:
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rake
|
111
|
+
version_requirement:
|
112
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.7.1
|
117
|
+
version:
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: activesupport
|
120
|
+
version_requirement:
|
121
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.3.1.5618
|
126
|
+
version:
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: activerecord
|
129
|
+
version_requirement:
|
130
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 1.14.4.5618
|
135
|
+
version:
|
136
|
+
- !ruby/object:Gem::Dependency
|
137
|
+
name: actionpack
|
138
|
+
version_requirement:
|
139
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: 1.12.5.5618
|
144
|
+
version:
|