crunchr 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "crunchr"
8
- s.version = "0.0.1"
8
+ s.version = "0.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Hartog C. de Mik"]
12
- s.date = "2013-02-08"
12
+ s.date = "2013-02-11"
13
13
  s.description = "Crunch statistics"
14
14
  s.email = "hartog@organisedminds.com"
15
15
  s.extra_rdoc_files = [
@@ -77,9 +77,29 @@ module Crunchr
77
77
 
78
78
  # given a string like 'keys - doors', returns the amount of spare keys
79
79
  #
80
+ # You can group calculations by surrounding them with (), eg:
81
+ #
82
+ # (doors - keys) / (inhabitants - keys)
83
+ #
84
+ # Pass in real numbers if you like
85
+ #
86
+ # (doors + 2) / keys
87
+ #
88
+ # @note
89
+ # The result is *always* a float.
90
+ # If anything fails, 0.0 is returned.
91
+ #
92
+ # @param String key The calculation to perform
93
+ # @return Float result
94
+ #
80
95
  def calculate(key)
81
- (left, op, right) = key.split(/\s/)
96
+ while key =~ /\(/ && key =~ /\)/
97
+ key.gsub!(/\(([^\(\)]+)\)/) do |calculation|
98
+ calculate(calculation.gsub(/[\(\)]/, ''))
99
+ end
100
+ end
82
101
 
102
+ (left, op, right) = key.split(/\s/)
83
103
 
84
104
  left = (
85
105
  left =~ /[^\d.]/ ? self.fetch(left) : BigDecimal.new(left)
@@ -101,33 +121,51 @@ module Crunchr
101
121
 
102
122
  module ClassMethods
103
123
  # pass in a list off data-objects with and get a nice table
104
- # list = [ Object.data({ doors: 1, keys: 2}), Object.data({ doors: 1, keys: 3 }, ... ]
124
+ # list = [ Object.data({ doors: 1, keys: 2}),
125
+ # Object.data({ doors: 1, keys: 3 },
126
+ # ...
127
+ # ]
105
128
  #
106
129
  # table = Object.as_table(list, keys: %w[doors keys])
107
130
  # # => [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 3, 8 ] ]
108
131
  #
109
132
  # Or use lists in lists
133
+ #
110
134
  # deep_list = [ list, list list ]
111
- # table = Object.as_table(deep_list, keys: %[doors keys], list_operator: delta)
112
- # # => [ [ 2, 6 ] ] (differnece of mac and min for both doors and keys)
135
+ # table = Object.as_table(
136
+ # deep_list, keys: %[doors keys], list_operator: delta
137
+ # )
138
+ # # => [ [ 2, 6 ] ] (difference of max and min for both doors and keys)
139
+ #
140
+ # == Usage with dates/times
141
+ #
142
+ # If you include Crunchr into something Active-Modely that has 'created_at'
143
+ # as a (sane) attribute, you can supply a :date key, it will add a column
144
+ # with the value of created_at into the table. If you do not supply
145
+ # :date_fmt, it will call #to_date on the column
113
146
  #
114
147
  # @param [Array] list List (1d or 2d) of data objects
115
148
  # @param [Hash] opts Options
116
149
  # @option opts [Array] keys List of keys to fetch, may contain
117
150
  # calculations, eg: ['doors', 'keys', 'doors / keys']
118
- # @option opts [Symbol] list_operator With a 2d list, what operator to
119
- # apply to each given list to determine the 1d value, any of
120
- # - :mean
121
- # - :stddev
122
- # - :median
123
- # - :range
124
- # - :mode
125
- # - :sum
126
- # - :min
127
- # - ;max
151
+ # @option opts [Symbol] list_operator With a 2d list, what operator to
152
+ # apply to each given list to determine the 1d value see #delta for
153
+ # more info
154
+ # @option opts [String] date_fmt Use as input to #strftime for the value
155
+ # in the date column
156
+ # @option opts [String] str_fmt Use as input to #sprintf for the value
157
+ # in **every** column. (Cannot be used together with :delta)
158
+ # @option opts [Boolean] delta After the first row, fill every other row
159
+ # with the difference to the previous row. (Cannot be used with
160
+ # :str_fmt)
161
+ #
128
162
  def as_table(list, opts = {})
129
163
  keys = opts[:keys] || raise("Need keys")
130
164
 
165
+ if opts[:delta] && opts[:str_fmt]
166
+ raise ":delta and :str_fmt cannot be supplied together"
167
+ end
168
+
131
169
  table = []
132
170
 
133
171
  list.each do |statistic|
@@ -188,6 +226,23 @@ module Crunchr
188
226
  return table
189
227
  end
190
228
 
229
+ # flatten an array of rows by applying an operator vertically on each
230
+ # column and accepting the result as a single row
231
+ #
232
+ # @param [Array] array List of lists
233
+ # @param [Hash] opts Options
234
+ # @option opts [Symbol] list_operator What operator to apply to the array
235
+ # to get a single value, defaults to :mean, should be any of
236
+ # - :mean
237
+ # - :stddev
238
+ # - :median
239
+ # - :range
240
+ # - :mode
241
+ # - :sum
242
+ # - :min
243
+ # - :max
244
+ # - :delta (takes the difference of max and min)
245
+ #
191
246
  def flatten(array, opts)
192
247
  keys = opts[:keys].dup
193
248
 
@@ -229,11 +284,16 @@ module Crunchr
229
284
  return [keys, collection]
230
285
  end
231
286
 
232
-
287
+ # Return a BigDecimal zero value
233
288
  def zero
234
289
  BigDecimal.new("0.0")
235
290
  end
236
291
 
292
+ # Make sure the value is zero if it is NaN, infinite, or nil
293
+ # Turn the value into a float if it is a BigDecimal
294
+ #
295
+ # @param value The value to check
296
+ # @return [Float, Integer] the improved value
237
297
  def checked(value)
238
298
  value = zero() if value.respond_to?(:nan?) && value.nan?
239
299
  value = zero() if value.respond_to?(:infinity?) && value.infinity?
@@ -164,4 +164,23 @@ describe "Crunchr" do
164
164
  end
165
165
 
166
166
  end
167
+
168
+ context "Extended calculations" do
169
+ before(:each) do
170
+ subject.data = deep_hash
171
+ end
172
+
173
+ it "performs calculations successively" do
174
+ subject.fetch("(loans/requested/GBP + loans/payed/GBP) - commission/pending/GBP").should == (0.65395 + 145.23) - 1.3079
175
+ end
176
+
177
+ it "handles nested groupings" do
178
+ subject.fetch("((users/count + users/active) + users/active) + users/count").should == (((14 + 2) + 2) + 14).to_f
179
+ end
180
+
181
+ it "ignores malformed groupings" do
182
+ subject.fetch("(users/count + users/active").should == 2.0 # 0 + 2
183
+ subject.fetch("((users/count + users/active) + 12").should == 12.0
184
+ end
185
+ end
167
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crunchr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-08 00:00:00.000000000 Z
12
+ date: 2013-02-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &16830960 !ruby/object:Gem::Requirement
16
+ requirement: &24262560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.12'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *16830960
24
+ version_requirements: *24262560
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: mocha
27
- requirement: &12972500 !ruby/object:Gem::Requirement
27
+ requirement: &24290860 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *12972500
35
+ version_requirements: *24290860
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rdoc
38
- requirement: &12974440 !ruby/object:Gem::Requirement
38
+ requirement: &24297100 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.12'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *12974440
46
+ version_requirements: *24297100
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
- requirement: &12969640 !ruby/object:Gem::Requirement
49
+ requirement: &24305540 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - =
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.2.1
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *12969640
57
+ version_requirements: *24305540
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jeweler
60
- requirement: &12977560 !ruby/object:Gem::Requirement
60
+ requirement: &24329100 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.8.4
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *12977560
68
+ version_requirements: *24329100
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rcov
71
- requirement: &12997100 !ruby/object:Gem::Requirement
71
+ requirement: &24375820 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *12997100
79
+ version_requirements: *24375820
80
80
  description: Crunch statistics
81
81
  email: hartog@organisedminds.com
82
82
  executables: []
@@ -114,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
114
  version: '0'
115
115
  segments:
116
116
  - 0
117
- hash: 1841846266649628700
117
+ hash: -971526191153423674
118
118
  required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements: