Permalink
Browse files

==0.1.14

  *Incorporated the Mustache templating library into aggregation definitions allowing expression to reference previously
   defined dimensions, measures, or named expressions (using the syntax: define :expression_name, 'this.my_expression != "that"')
   Allows for definitions such as this:  avg :average_happy_defects, :expression=>'{{defects}} && this.type=="Happy"'
   Also available is 'time_now' to represent the Ruby's Time.now in the form of a javascript date, so '{{time_now}}' will
   become 'new Date(2342342)' where 2342342 is Time.now.to_i
  • Loading branch information...
1 parent 66cef5c commit 5cd9592cca79dfb58fdbb28b3eb686094a6728b1 @PlasticLizard committed Apr 16, 2010
View
@@ -1,3 +1,10 @@
+==0.1.14
+ *Incorporated the Mustache templating library into aggregation definitions allowing expression to reference previously
+ defined dimensions, measures, or named expressions (using the syntax: define :expression_name, 'this.my_expression != "that"')
+ Allows for definitions such as this: avg :average_happy_defects, :expression=>'{{defects}} && this.type=="Happy"'
+ Also available is 'time_now' to represent the Ruby's Time.now in the form of a javascript date, so '{{time_now}}' will
+ become 'new Date(2342342)' where 2342342 is Time.now.to_i
+
==0.1.13
*Previous feature broke ratios. Fixed now.
View
@@ -5,7 +5,7 @@
Gem::Specification.new do |s|
s.name = %q{cubicle}
- s.version = "0.1.13"
+ s.version = "0.1.14"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Nathan Stults"]
@@ -33,8 +33,8 @@ def dimension_names
def find_member(member_name)
@dimensions[member_name] ||
@measures[member_name]
- end
-
+ end
+
def query(*args,&block)
options = args.extract_options!
query = Cubicle::Query.new(self)
@@ -142,10 +142,12 @@ def ensure_indexes(collection_name,dimension_names)
end
def aggregate(query,options={})
+ view = AggregationView.new(aggregation)
+
map, reduce = MapReduceHelper.generate_map_function(query), MapReduceHelper.generate_reduce_function
- map = Mustache.render(map, AggregationView.new(aggregation)) if aggregation
+
options[:finalize] = MapReduceHelper.generate_finalize_function(query)
- options["query"] = prepare_filter(query,options[:where] || {})
+ options["query"] = expand_template(prepare_filter(query,options[:where] || {}),view)
query.source_collection_name = options.delete(:source_collection) || query.source_collection_name || aggregation.source_collection_name
@@ -157,13 +159,23 @@ def aggregate(query,options={})
#This is defensive - some tests run without ever initializing any collections
return [] unless database.collection_names.include?(query.source_collection_name)
- result = database[query.source_collection_name].map_reduce(map,reduce,options)
+ result = database[query.source_collection_name].map_reduce(expand_template(map, view),reduce,options)
ensure_indexes(target_collection,query.dimension_names) if target_collection
result
end
+ def expand_template(template,view)
+ return "" unless template
+ return Mustache.render(template,view) if template.is_a?(String)
+ if (template.is_a?(Hash))
+ template.each {|key,val|template[key] = expand_template(val,view)}
+ return template
+ end
+ template
+ end
+
def prepare_filter(query,filter={})
filter.merge!(query.where) if query.respond_to?(:where) && query.where
filter.stringify_keys!
@@ -1,10 +1,28 @@
module Cubicle
module Aggregation
- class AggregationView < Mustache
+ class AggregationView < HashWithIndifferentAccess
attr_accessor :aggregation
def initialize(aggregation)
- @aggregation = aggregation
+
+ super[:time_now] = "new Date(#{Time.now.to_i*1000})"
+
+ self.merge!(aggregation.named_expressions)
+
+ list = aggregation.measures + aggregation.dimensions
+ list.each do |m|
+ super[m.name] = m.expression
+ end
+ self.each do |key,value|
+ self[key] = expand_template(value)
+ end
+ end
+
+ def expand_template(template)
+ while (template =~ /\{\{\w+\}\}/)
+ template = Mustache.render(template,self)
+ end
+ template
end
end
end
@@ -18,7 +18,11 @@ def target_collection_name(collection_name = nil)
def dimension(*args)
dimensions << Cubicle::Dimension.new(*args)
dimensions[-1]
- end
+ end
+
+ def named_expressions
+ return @named_expressions ||= OrderedHashWithIndifferentAccess.new
+ end
def dimensions(*args)
return (@dimensions ||= Cubicle::MemberList.new) if args.length < 1
@@ -119,6 +123,10 @@ def time_dimension(*args)
alias time_dimension= time_dimension
alias date time_dimension
alias time time_dimension
+
+ def define(name,expression)
+ named_expressions[name] = expression
+ end
end
end
end
@@ -1,22 +1,24 @@
class DefectCubicle
extend Cubicle::Aggregation
+ define :preventable, "this.root_cause != 'act_of_god'"
+ define :product_name, "this.product.name"
+
date :manufacture_date, :field_name=>'manufacture_date', :alias=>:date
dimension :month, :expression=>'this.manufacture_date.substring(0,7)'
dimension :year, :expression=>'this.manufacture_date.substring(0,4)'
dimension :manufacture_time
- dimension :product, :field_name=>'product.name'
+ dimension :product, :expression=>'{{product_name}}'
dimension :region, :field_name=>'plant.address.region'
dimensions :operator, :outcome
-
count :total_defects, :field_name=>'defect_id'
- count :distinct_products, :field_name=>'product.name', :distinct=>true
- count :preventable_defects, :expression=>'this.root_cause != "act_of_god"'
- count :conditioned_preventable,:expression=>'1.0', :condition=>'this.root_cause != "act_of_god"'
+ count :distinct_products, :expression=>'{{product}}', :distinct=>true
+ count :preventable_defects, :expression=>'{{preventable}}'
+ count :conditioned_preventable,:expression=>'1.0', :condition=>'{{preventable}}'
sum :total_cost, :field_name=>'cost'
avg :avg_cost, :field_name=>'cost'

0 comments on commit 5cd9592

Please sign in to comment.