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