gorillib 0.4.1pre → 0.4.2pre

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.
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