gorillib 0.4.1pre → 0.4.2pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/.gitignore +13 -10
  2. data/.rspec +1 -1
  3. data/.yardopts +1 -0
  4. data/CHANGELOG.md +47 -0
  5. data/Gemfile +22 -19
  6. data/Guardfile +23 -9
  7. data/README.md +12 -12
  8. data/Rakefile +29 -40
  9. data/VERSION +1 -1
  10. data/examples/benchmark/factories_benchmark.rb +87 -0
  11. data/examples/builder/ironfan.rb +1 -19
  12. data/examples/hash/slicing_methods.rb +101 -0
  13. data/gorillib.gemspec +36 -35
  14. data/lib/gorillib/array/deep_compact.rb +4 -3
  15. data/lib/gorillib/array/simple_statistics.rb +76 -0
  16. data/lib/gorillib/base.rb +0 -1
  17. data/lib/gorillib/builder.rb +15 -30
  18. data/lib/gorillib/collection.rb +159 -57
  19. data/lib/gorillib/collection/model_collection.rb +136 -43
  20. data/lib/gorillib/datetime/parse.rb +4 -2
  21. data/lib/gorillib/{array → deprecated/array}/average.rb +0 -0
  22. data/lib/gorillib/{array → deprecated/array}/random.rb +2 -1
  23. data/lib/gorillib/{array → deprecated/array}/sorted_median.rb +0 -0
  24. data/lib/gorillib/{array → deprecated/array}/sorted_percentile.rb +0 -0
  25. data/lib/gorillib/deprecated/array/sorted_sample.rb +13 -0
  26. data/lib/gorillib/{metaprogramming → deprecated/metaprogramming}/aliasing.rb +0 -0
  27. data/lib/gorillib/enumerable/sum.rb +3 -3
  28. data/lib/gorillib/exception/raisers.rb +92 -22
  29. data/lib/gorillib/factories.rb +550 -0
  30. data/lib/gorillib/hash/mash.rb +15 -58
  31. data/lib/gorillib/hashlike/deep_compact.rb +2 -2
  32. data/lib/gorillib/hashlike/slice.rb +55 -40
  33. data/lib/gorillib/model.rb +5 -3
  34. data/lib/gorillib/model/base.rb +33 -119
  35. data/lib/gorillib/model/defaults.rb +58 -14
  36. data/lib/gorillib/model/errors.rb +10 -0
  37. data/lib/gorillib/model/factories.rb +1 -367
  38. data/lib/gorillib/model/field.rb +40 -18
  39. data/lib/gorillib/model/fixup.rb +16 -0
  40. data/lib/gorillib/model/positional_fields.rb +35 -0
  41. data/lib/gorillib/model/schema_magic.rb +162 -0
  42. data/lib/gorillib/model/serialization.rb +1 -2
  43. data/lib/gorillib/model/serialization/csv.rb +59 -0
  44. data/lib/gorillib/pathname.rb +19 -8
  45. data/lib/gorillib/some.rb +2 -0
  46. data/lib/gorillib/string/constantize.rb +17 -10
  47. data/lib/gorillib/string/inflector.rb +11 -7
  48. data/lib/gorillib/type/boolean.rb +40 -0
  49. data/lib/gorillib/type/extended.rb +76 -40
  50. data/lib/gorillib/type/url.rb +6 -4
  51. data/lib/gorillib/utils/console.rb +1 -18
  52. data/lib/gorillib/utils/edge_cases.rb +18 -0
  53. data/spec/examples/builder/ironfan_spec.rb +5 -10
  54. data/spec/gorillib/array/compact_blank_spec.rb +36 -21
  55. data/spec/gorillib/array/simple_statistics_spec.rb +143 -0
  56. data/spec/gorillib/builder_spec.rb +16 -20
  57. data/spec/gorillib/collection_spec.rb +131 -35
  58. data/spec/gorillib/exception/raisers_spec.rb +39 -0
  59. data/spec/gorillib/hash/deep_compact_spec.rb +3 -3
  60. data/spec/gorillib/model/{record/defaults_spec.rb → defaults_spec.rb} +5 -1
  61. data/spec/gorillib/model/factories_spec.rb +335 -0
  62. data/spec/gorillib/model/{record/overlay_spec.rb → overlay_spec.rb} +0 -0
  63. data/spec/gorillib/model/serialization_spec.rb +2 -2
  64. data/spec/gorillib/model_spec.rb +19 -18
  65. data/spec/gorillib/pathname_spec.rb +7 -7
  66. data/spec/gorillib/string/truncate_spec.rb +3 -13
  67. data/spec/gorillib/type/extended_spec.rb +50 -2
  68. data/spec/gorillib/utils/capture_output_spec.rb +1 -1
  69. data/spec/spec_helper.rb +10 -7
  70. data/spec/support/factory_test_helpers.rb +76 -0
  71. data/spec/support/gorillib_test_helpers.rb +36 -24
  72. data/spec/support/model_test_helpers.rb +39 -2
  73. metadata +86 -51
  74. data/lib/alt/kernel/call_stack.rb +0 -56
  75. data/lib/gorillib/array/sorted_sample.rb +0 -12
  76. data/lib/gorillib/builder/field.rb +0 -5
  77. data/lib/gorillib/collection/has_collection.rb +0 -31
  78. data/lib/gorillib/collection/list_collection.rb +0 -58
  79. data/lib/gorillib/exception/confidence.rb +0 -17
  80. data/lib/gorillib/io/system_helpers.rb +0 -30
  81. data/lib/gorillib/model/record_schema.rb +0 -9
  82. data/lib/gorillib/utils/stub_module.rb +0 -33
  83. data/spec/array/average_spec.rb +0 -24
  84. data/spec/array/sorted_median_spec.rb +0 -18
  85. data/spec/array/sorted_percentile_spec.rb +0 -24
  86. data/spec/array/sorted_sample_spec.rb +0 -28
  87. data/spec/gorillib/metaprogramming/aliasing_spec.rb +0 -180
  88. data/spec/gorillib/model/record/factories_spec.rb +0 -335
  89. data/spec/support/kcode_test_helper.rb +0 -16
@@ -14,3 +14,5 @@ require 'gorillib/string/constantize'
14
14
  require 'gorillib/hash/mash'
15
15
  require 'gorillib/metaprogramming/delegation'
16
16
  require 'gorillib/metaprogramming/concern'
17
+
18
+ require 'gorillib/logger/log'
@@ -1,28 +1,35 @@
1
1
  require 'gorillib/string/inflector'
2
2
  class String
3
3
 
4
- # +constantize+ tries to find a declared constant with the name specified
5
- # in the string. It raises a NameError when the name is not in CamelCase
6
- # or is not initialized. See Gorillib::Inflector.constantize
4
+ # Find a declared constant with the name specified in the string, or raise.
7
5
  #
8
- # Examples
6
+ # @example
9
7
  # "Module".constantize # => Module
10
8
  # "Class".constantize # => Class
11
9
  # "blargle".constantize # => NameError: wrong constant name blargle
10
+ #
11
+ # @raise [NameError] when the name is not in CamelCase or is not initialized.
12
+ # @return [Module,Class] the specified class
13
+ # @see Gorillib::Inflector.constantize
12
14
  def constantize
13
15
  Gorillib::Inflector.constantize(self)
14
- end unless method_defined?(:constantize)
16
+ end
15
17
 
16
- # +safe_constantize+ tries to find a declared constant with the name specified
17
- # in the string. It returns nil when the name is not in CamelCase
18
- # or is not initialized. See Gorillib::Model::Inflector.safe_constantize
18
+ # Find a declared constant with the name specified in the string, or return nil.
19
+ #
20
+ # @return [Module,Class] the specified class, or nil when the name is not in
21
+ # CamelCase or is not initialized.
19
22
  #
20
- # Examples
23
+ # @example
21
24
  # "Module".safe_constantize # => Module
22
25
  # "Class".safe_constantize # => Class
23
26
  # "blargle".safe_constantize # => nil
27
+ #
28
+ # @see Gorillib::Model::Inflector.safe_constantize
29
+ # @return [Module,Class] the specified constant,
30
+ # or nil when the name is not in CamelCase or is not initialized.
24
31
  def safe_constantize
25
32
  Gorillib::Inflector.safe_constantize(self)
26
- end unless method_defined?(:safe_constantize)
33
+ end
27
34
 
28
35
  end
@@ -120,14 +120,16 @@ module Gorillib::Inflector
120
120
  Object.module_eval("::#{$1}", __FILE__, __LINE__)
121
121
  end
122
122
 
123
- # Tries to find a constant with the name specified in the argument string:
123
+ # Tries to find a declared constant with the name specified in the string, or return nil.
124
124
  #
125
- # "Module".safe_constantize # => Module
126
- # "Test::Unit".safe_constantize # => Test::Unit
125
+ # @example
126
+ # "Module".safe_constantize # => Module
127
+ # "Class".safe_constantize # => Class
127
128
  #
128
- # The name is assumed to be the one of a top-level constant, no matter whether
129
+ # The `name` is assumed to be the one of a top-level constant, no matter whether
129
130
  # it starts with "::" or not. No lexical context is taken into account:
130
131
  #
132
+ # @example finds the top-level constant `::C`, not `::M::C`
131
133
  # C = 'outside'
132
134
  # module M
133
135
  # C = 'inside'
@@ -135,13 +137,15 @@ module Gorillib::Inflector
135
137
  # "C".safe_constantize # => 'outside', same as ::C
136
138
  # end
137
139
  #
138
- # nil is returned when the name is not in CamelCase or the constant (or part of it) is
139
- # unknown.
140
- #
140
+ # @example nil is returned when the name is not in CamelCase or the constant (or part of it) is unknown.
141
141
  # "blargle".safe_constantize # => nil
142
142
  # "UnknownModule".safe_constantize # => nil
143
143
  # "UnknownModule::Foo::Bar".safe_constantize # => nil
144
144
  #
145
+ #
146
+ # @see String.safe_constantize
147
+ # @return [Module,Class] the specified constant,
148
+ # or nil when the name is not in CamelCase or is not initialized.
145
149
  def safe_constantize(camel_cased_word)
146
150
  begin
147
151
  constantize(camel_cased_word)
@@ -0,0 +1,40 @@
1
+
2
+ # require 'gorillib/metaprogramming/delegation'
3
+ #
4
+ # class ::Boolean < ::Object
5
+ # attr_accessor :val
6
+ # def initialize(val=nil)
7
+ # self.val = val
8
+ # end
9
+ #
10
+ # delegate :!, :to_s, :blank?, :frozen?, :nil?, :present?, :to => :val
11
+ # delegate :!=, :&, :<=>, :=~, :^, :|, :to => :val
12
+ #
13
+ # def inspect()
14
+ # "<Boolean #{val.inspect}>"
15
+ # end
16
+ # def try_dup()
17
+ # ::Boolean.new(val)
18
+ # end
19
+ #
20
+ # def self.true ; self.new(true) ; end
21
+ # def self.false ; self.new(false) ; end
22
+ #
23
+ # def is_a?(klass) val.is_a?(klass) || super ; end
24
+ # def kind_of?(klass) val.kind_of?(klass) || super ; end
25
+ # def instance_of?(klass) val.instance_of?(klass) || super ; end
26
+ #
27
+ # def !=(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val != other_val) ; end
28
+ # def ==(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val == other_val) ; end
29
+ # def ===(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val === other_val) ; end
30
+ # def <=>(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val <=> other_val) ; end
31
+ # def eql?(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val.eql? other_val) ; end
32
+ # def equal?(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val.equal? other_val) ; end
33
+ #
34
+ # end
35
+
36
+ # Datamapper also defines:
37
+ #
38
+ # Apikey BCryptHash Decimal URI UUID Slug CommaSeparatedList Csv IpAddress Json Yaml Enum Flag Discriminator
39
+ #
40
+ # maybe someday we will too...
@@ -1,5 +1,7 @@
1
1
  require 'pathname'
2
- require 'time'
2
+ require 'date'
3
+ require 'set'
4
+ require 'gorillib/factories'
3
5
 
4
6
  class ::Long < ::Integer ; end
5
7
  class ::Double < ::Float ; end
@@ -9,42 +11,76 @@ class ::Guid < ::String ; end
9
11
  class ::IpAddress < ::String ; end
10
12
  class ::Hostname < ::String ; end
11
13
 
12
- # require 'gorillib/metaprogramming/delegation'
13
- #
14
- # class ::Boolean < ::Object
15
- # attr_accessor :val
16
- # def initialize(val=nil)
17
- # self.val = val
18
- # end
19
- #
20
- # delegate :!, :to_s, :blank?, :frozen?, :nil?, :present?, :to => :val
21
- # delegate :!=, :&, :<=>, :=~, :^, :|, :to => :val
22
- #
23
- # def inspect()
24
- # "<Boolean #{val.inspect}>"
25
- # end
26
- # def try_dup()
27
- # ::Boolean.new(val)
28
- # end
29
- #
30
- # def self.true ; self.new(true) ; end
31
- # def self.false ; self.new(false) ; end
32
- #
33
- # def is_a?(klass) val.is_a?(klass) || super ; end
34
- # def kind_of?(klass) val.kind_of?(klass) || super ; end
35
- # def instance_of?(klass) val.instance_of?(klass) || super ; end
36
- #
37
- # def !=(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val != other_val) ; end
38
- # def ==(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val == other_val) ; end
39
- # def ===(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val === other_val) ; end
40
- # def <=>(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val <=> other_val) ; end
41
- # def eql?(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val.eql? other_val) ; end
42
- # def equal?(other_val) other_val = other_val.val if other_val.is_a?(::Boolean) ; (val.equal? other_val) ; end
43
- #
44
- # end
45
-
46
- # Datamapper also defines:
47
- #
48
- # Apikey BCryptHash Decimal URI UUID Slug CommaSeparatedList Csv IpAddress Json Yaml Enum Flag Discriminator
49
- #
50
- # maybe someday we will too...
14
+ class ::EpochTime < ::Integer ; end
15
+ class ::IntTime < ::EpochTime ; end
16
+
17
+ module Gorillib
18
+ module Factory
19
+ class GuidFactory < StringFactory ; self.product = ::Guid ; register_factory! ; end
20
+ class HostnameFactory < StringFactory ; self.product = ::Hostname ; register_factory! ; end
21
+ class IpAddressFactory < StringFactory ; self.product = ::IpAddress ; register_factory! ; end
22
+
23
+ class DateFactory < ConvertingFactory
24
+ self.product = Date
25
+ FLAT_DATE_RE = /\A\d{8}Z?\z/
26
+ register_factory!
27
+ #
28
+ def convert(obj)
29
+ case obj
30
+ when FLAT_DATE_RE then product.new(obj[0..3].to_i, obj[4..5].to_i, obj[6..7].to_i)
31
+ when Time then Date.new(obj.year, obj.month, obj.day)
32
+ when String then Date.parse(obj)
33
+ else mismatched!(obj)
34
+ end
35
+ rescue ArgumentError => err
36
+ raise if err.is_a?(TypeMismatchError)
37
+ warn "Cannot parse time #{obj}: #{err}"
38
+ return nil
39
+ end
40
+ end
41
+
42
+ class EpochTimeFactory < ConvertingFactory
43
+ self.product = Integer
44
+ def self.typename() :epoch_time ; end
45
+ register_factory! :epoch_time, EpochTime
46
+ #
47
+ def convert(obj)
48
+ case obj
49
+ when Numeric then obj.to_f
50
+ when Time then obj.to_f
51
+ when /\A\d{14}Z?\z/ then Time.parse(obj)
52
+ when String then Time.parse_safely(obj).to_f
53
+ end
54
+ end
55
+ end
56
+
57
+ class IntTimeFactory < EpochTimeFactory
58
+ def self.typename() :int_time ; end
59
+ register_factory! :int_time, IntTime
60
+ #
61
+ def convert(obj)
62
+ result = super
63
+ result.nil? ? nil : result.to_i
64
+ end
65
+ end
66
+
67
+ class Boolean10Factory < BooleanFactory
68
+ def self.typename() :boolean_10 ; end
69
+ register_factory! :boolean_10
70
+ #
71
+ def convert(obj)
72
+ case obj.to_s
73
+ when "0" then false
74
+ when "1" then true
75
+ else super
76
+ end
77
+ end
78
+ end
79
+
80
+ class SetFactory < EnumerableFactory
81
+ self.product = Set
82
+ register_factory!
83
+ end
84
+
85
+ end
86
+ end
@@ -2,8 +2,10 @@ require 'addressable/uri'
2
2
 
3
3
  class ::Url < Addressable::URI ; end
4
4
 
5
- class UrlFactory < Gorillib::Factory::ConvertingFactory
6
- self.product = ::Url
7
- def convert(obj) product.parse(obj) ; end
8
- register_factory!
5
+ module Gorillib::Factory
6
+ class UrlFactory < Gorillib::Factory::ConvertingFactory
7
+ self.product = ::Url
8
+ def convert(obj) product.parse(obj) ; end
9
+ register_factory!
10
+ end
9
11
  end
@@ -36,7 +36,7 @@ class ItsATrap < BasicObject
36
36
  alias_method :pretty_inspect, :inspect
37
37
  def methods() @obj.methods ; end
38
38
 
39
- # @returns the proxied object
39
+ # @return the proxied object
40
40
  def __obj__ ; @obj ; end
41
41
 
42
42
  # These are defined on BasicObject, delegate them along with the rest
@@ -70,23 +70,6 @@ private
70
70
  end
71
71
  end
72
72
 
73
- module Test
74
- module_function
75
-
76
- def create_class(name, *args, &block)
77
- Object.class_eval do
78
- remove_const(name) if self.const_defined?(name)
79
- const_set(name, Class.new(*args, &block))
80
- end
81
- end
82
-
83
- def example_singleton(name, *args)
84
- require 'singleton'
85
- create_class(name, *args){ include ::Singleton }
86
- end
87
-
88
- end
89
-
90
73
  class Module
91
74
 
92
75
  #
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Gorillib
3
+ module Utils
4
+ module EdgeCases
5
+ # Thanks http://www.geertvanderploeg.com/unicode-gen/ !
6
+ # For more fun, see http://www.tamasoft.co.jp/en/general-info/unicode.html
7
+ STRINGS = {
8
+ :unicode_smileys => '٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃).',
9
+ :internationalization => 'Intèrnatiònãlïzãtíon',
10
+ :html_unsafe => 'Testing «Mònkêy Sí Monkè Dü»: 1<2 & 4+1>3, now 20% off!',
11
+
12
+ :eastern_arabic_digits => '٠١٢٣٤٥٦٧٨٩',
13
+ :bengali_digits => '০১২৩৪৫৬৭৮৯',
14
+ }
15
+
16
+ end
17
+ end
18
+ end
@@ -1,21 +1,16 @@
1
1
  require 'spec_helper'
2
- # related libs
3
- require 'gorillib/model'
4
- require 'gorillib/model/field'
5
- require 'gorillib/model/defaults'
6
- # libs under test
7
- require 'gorillib/builder'
8
- require 'gorillib/builder/field'
9
- require 'gorillib/string/simple_inflector'
10
- # testing helpers
11
2
 
12
- load GORILLIB_ROOT_DIR('examples/builder/ironfan.rb')
3
+ require 'gorillib/builder'
13
4
 
14
5
  module Gorillib::Test ; end
15
6
  module Meta::Gorillib::Test ; end
16
7
 
17
8
  describe Gorillib::Builder, :model_spec => true do
9
+ before do
10
+ require_relative '../../../examples/builder/ironfan.rb'
11
+ end
18
12
  after(:each){ Gorillib::Test.nuke_constants ; Meta::Gorillib::Test.nuke_constants }
13
+
19
14
  def example_cluster
20
15
  Gorillib::Test.cluster
21
16
  end
@@ -1,37 +1,52 @@
1
1
  require 'spec_helper'
2
2
  require 'gorillib/array/compact_blank'
3
3
 
4
- describe Array, :simple_spec => true do
4
+ describe Array, :simple_spec do
5
+ let(:blankish){ mock('blankish', :blank? => true) }
6
+ let(:nonblank){ mock('nonblank', :blank? => false) }
5
7
 
6
8
  describe '#compact_blank' do
7
- it 'with empty' do
8
- [ [nil], [nil, false, {}, ""] ].each do |arr|
9
- arr.compact_blank.should == []
10
- arr.length.should_not == 0
11
- end
9
+ it 'omits nils, like #compact' do
10
+ arr = [nil]
11
+ arr.compact_blank.should == []
12
+ arr.length.should == 1
13
+ end
14
+ it "also omits false, {}, '' and anything else #blank\?" do
15
+ arr = [nil, false, {}, "", blankish]
16
+ arr.compact_blank.should == []
17
+ arr.length.should == 5
12
18
  end
13
19
 
14
- it 'with full' do
15
- [ [nil, 1, nil, 2], [nil, 1, false, 2, {}, ""] ].each do |arr|
16
- arr.compact_blank.should == [1, 2]
17
- end
20
+ it 'preserves non-blank elements' do
21
+ arr = [nil, 1, nil, 2]
22
+ arr.compact_blank.should == [1, 2]
23
+ arr.length.should == 4
24
+ arr = [nil, 1, false, 2, {}, ""]
25
+ arr.compact_blank.should == [1, 2]
26
+ arr.length.should == 6
18
27
  end
19
28
  end
20
29
 
21
30
  describe '#compact_blank!' do
22
- it 'with empty' do
23
- [].compact_blank!.should == []
24
- [ [nil], [nil, false, {}, ""] ].each do |arr|
25
- arr.compact_blank!.should == []
26
- arr.length.should == 0
27
- end
31
+ it 'removes nils in-place, like #compact!' do
32
+ arr = [nil]
33
+ arr.compact_blank!.should == []
34
+ arr.length.should == 0
28
35
  end
29
-
30
- it 'with full' do
31
- [ [nil, 1, nil, 2], [nil, 1, false, 2, {}, ""] ].each do |arr|
32
- arr.compact_blank!.should == [1, 2]
33
- end
36
+ it "removes false, {}, '' and anything else #blank\?" do
37
+ arr = [nil, false, {}, "", blankish]
38
+ arr.compact_blank!.should == []
39
+ arr.length.should == 0
34
40
  end
35
41
 
42
+ it 'preserves non-blank elements' do
43
+ arr = [nil, 1, nil, 2]
44
+ arr.compact_blank!.should == [1, 2]
45
+ arr.length.should == 2
46
+ arr = [nil, 1, false, 2, {}, ""]
47
+ arr.compact_blank!.should == [1, 2]
48
+ arr.length.should == 2
49
+ end
36
50
  end
51
+
37
52
  end
@@ -0,0 +1,143 @@
1
+ require 'spec_helper'
2
+ require 'gorillib/array/simple_statistics'
3
+
4
+ describe Array, :simple_spec do
5
+ let(:seven_squares){ [ 1, 4, 9, 16, 25, 36, 49] }
6
+ let(:five_squares ){ [ 1, 4, 16, 36, 49 ] }
7
+ let(:one_element ){ [ 16 ] }
8
+ subject{ seven_squares }
9
+
10
+ describe '#average' do
11
+ it('is nil for empty array'){ [].average.should be_nil }
12
+ it('is the numeric mean') do
13
+ five_squares.average.should eql 21.2
14
+ seven_squares.average.should eql 20.0
15
+ one_element.average.should eql 16.0
16
+ end
17
+ it('is is uptyped by its elements') do
18
+ five_squares.map(&:to_f).average.should eql 21.2
19
+ [ Complex(1.0,2.0), Complex(3,4), Complex(5) ].average.should eql Complex(3.0,2.0)
20
+ [ Complex(1,2), Complex(3,4), Complex(5) ].average.should eql (Complex(9,6) / 3.0)
21
+ end
22
+ context 'on non-float array element' do
23
+ it 'raises error' do
24
+ expect{ [0.0, :b, 1.0].average }.to raise_error(TypeError, /:b can\'t be coerced into Float/)
25
+ end
26
+ end
27
+ context 'with empty' do
28
+ it 'returns nil' do
29
+ [].average.should be_nil
30
+ end
31
+ end
32
+ context 'given a numerical array' do
33
+ it 'returns the average of the elements' do
34
+ (1..10).to_a.average.should == 5.5
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#at_fraction' do
40
+ it('is nil for empty array'){ [].at_fraction(0.5).should be_nil }
41
+ it 'indexes correctly' do
42
+ seven_squares.at_fraction(0 ).should == 1
43
+ seven_squares.at_fraction(0.5).should == 16
44
+ seven_squares.at_fraction(1.0).should == 49
45
+ five_squares.at_fraction(0.75).should == 36
46
+ end
47
+ it 'returns the value the nth of the way along' do
48
+ (0..6).map{|nth| seven_squares.at_fraction(nth/6.0).should == seven_squares[nth] }
49
+ end
50
+
51
+ it 'raises if the fraction is not a number between 0.0 and 1.0' do
52
+ expect{ subject.at_fraction(1.0001) }.to raise_error(ArgumentError, /between 0.0 and 1\.0: got 1\.0001/)
53
+ expect{ subject.at_fraction(-1) }.to raise_error(ArgumentError, /between 0.0 and 1\.0: got -1/)
54
+ expect{ subject.at_fraction('1.1') }.to raise_error(ArgumentError, /between 0.0 and 1\.0: got \"1.1\"/)
55
+ end
56
+ end
57
+
58
+ describe '#take_nths' do
59
+ it 'gives elements at 0.5/n, 1.5/n, 2.5/n ... n-0.5/n' do
60
+ five_squares.take_nths(2).should == [ 4, 36 ]
61
+ five_squares.take_nths(3).should == [ 4, 16, 36 ]
62
+ five_squares.take_nths(4).should == [ 4, 16, 36,49]
63
+ five_squares.take_nths(5).should == [1, 4, 16, 36,49]
64
+ seven_squares.take_nths(1).should == [ 16 ]
65
+ seven_squares.take_nths(2).should == [ 9, 36 ]
66
+ seven_squares.take_nths(3).should == [ 4, 16, 36 ]
67
+ seven_squares.take_nths(4).should == [ 4, 9, 25,36 ]
68
+ seven_squares.take_nths(5).should == [ 4, 9,16,25,36 ]
69
+ seven_squares.take_nths(6).should == [ 4, 9,16,25,36,49]
70
+ seven_squares.take_nths(99).should == seven_squares
71
+ seven_squares.take_nths(0).should == []
72
+ [].take_nths(3).should == []
73
+ end
74
+ context 'with empty' do
75
+ it 'returns an empty array' do
76
+ [].take_nths(1).should be_empty
77
+ end
78
+ end
79
+ context 'given any array' do
80
+ it ('returns a sample of the given size as close to evenly ' \
81
+ 'distributed over the array as possible') do
82
+ nths = (1..100).to_a.take_nths(26)
83
+ deltas = nths[0..-2].zip(nths[1..-1]).map{|a,b| b-a}
84
+ deltas.max.should <= 4
85
+ deltas.min.should >= 3
86
+ end
87
+ end
88
+ context 'given an undersized array' do
89
+ it 'does not return the same element more than once' do
90
+ ("a".."z").to_a.take_nths(27).should == ("a".."z").to_a
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#sorted_median' do
96
+ it 'takes the halfway-th element' do
97
+ five_squares.sorted_median.should eql 16
98
+ seven_squares.sorted_median.should eql 16
99
+ end
100
+ context 'with empty' do
101
+ it 'returns nil' do
102
+ [].sorted_median.should be_nil
103
+ end
104
+ end
105
+ context 'given any array' do
106
+ it 'returns the middle element of odd-sized arrays' do
107
+ ("a".."y").to_a.sorted_median.should == "m"
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#sorted_percentile' do
113
+ context 'with empty' do
114
+ it 'returns nil' do
115
+ [].sorted_percentile(0.0).should be_nil
116
+ end
117
+ end
118
+
119
+ context 'given any array' do
120
+ it 'returns the element closest to the given percentile' do
121
+ ("a".."y").to_a.sorted_percentile( 0.0).should == "a"
122
+ ("a".."y").to_a.sorted_percentile( 50.0).should == "m"
123
+ ("a".."y").to_a.sorted_percentile(100.0).should == "y"
124
+ end
125
+ end
126
+
127
+ # (Please do not define behavior for two elements equally close to
128
+ # a given percentile.)
129
+
130
+ it 'takes fractional index at pct/100' do
131
+ five_squares .sorted_percentile(50).should eql 16
132
+ seven_squares.sorted_percentile(10).should eql 4
133
+ seven_squares.sorted_percentile(50).should eql 16
134
+ seven_squares.sorted_percentile(90).should eql 36
135
+ seven_squares.sorted_percentile(100).should eql 49
136
+ end
137
+ it 'returns nil on empty array' do
138
+ [].sorted_median.should be nil
139
+ end
140
+ end
141
+
142
+
143
+ end