powerpack 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c7a89e31c15ac1b39a4806f126dbafd5cfe9991
4
- data.tar.gz: f779a0d32f87717f9353a0b3ccd3d48d9177c3b4
3
+ metadata.gz: 369199242dfe319d0e8afa477689f8d97cb0e647
4
+ data.tar.gz: 42c792d527998796fdce340364c9664e2b666a54
5
5
  SHA512:
6
- metadata.gz: 0e7a88b34938e7d2484a81c23c8e2b343c65bdfcf826a08201f9687edfb09b718e466333001ee0c45b1391845422298039c791f722ad5e23c2dd4970c781694c
7
- data.tar.gz: c00496df953ae329a3a61a96df857fde953cd490a6ad0ca8057db44be8f919ef63c97640eb7f644e8ef33ff839e1fcdbaa5ecc51f1559e9239b2376f75aca275
6
+ metadata.gz: 31cc13c64e5aec8afb3e2095d64c6047f484ec2390355c35fb36394c5f6818a61872cb582eba2d208485647a6e7e2137c7e2ea429cb0df953574f5ac14e728b3
7
+ data.tar.gz: 35eb68213ac9c8354566772c5b75c6bdde2be58155b455cd00209703473065f7f1a71e0a809b031cfe39e45e4df52405181d9cb878cc8af86719fc1a546a0fd3
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## master (unreleased)
4
+
5
+ ## 0.0.8 (13/09/2013)
6
+
7
+ ### New features
8
+
9
+ * Added `Enumerable#exactly?`
10
+ * Added `Enumerable#average`
11
+ * Added `Numeric#hundred`
12
+ * Added `Numeric#thousand`
13
+ * Added `Numeric#million`
14
+ * Added `Numeric#billion`
15
+ * Added `Numeric#trillion`
16
+ * Added `Numeric#quadrillion`
17
+
18
+ ### Changes
19
+
20
+ * Renamed `Array#tail` to `Array#butfirst`
21
+ * `Enumerable#take_last_while` returns the result in the same order as `Enumerable#take_last`
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Powerpack
5
5
 
6
- Powerpack offers some useful extensions to the standard Ruby classes (kind of like `ActiveSupport`, but less ambitious).
6
+ Powerpack offers some useful extensions to the standard Ruby classes (kind of like `ActiveSupport`, but less ambitious and more modular).
7
7
 
8
8
  ## Installation
9
9
 
@@ -22,11 +22,13 @@ Or install it yourself as:
22
22
  ## Extensions
23
23
 
24
24
  * [Array](http://rdoc.info/github/bbatsov/powerpack/Array)
25
+ * [#butfirst](http://rdoc.info/github/bbatsov/powerpack/Array#butfirst-instance_method)
25
26
  * [#butlast](http://rdoc.info/github/bbatsov/powerpack/Array#butlast-instance_method)
26
- * [#tail](http://rdoc.info/github/bbatsov/powerpack/Array#tail-instance_method)
27
27
  * [Enumerable](http://rdoc.info/github/bbatsov/powerpack/Enumerable)
28
+ * [#average](http://rdoc.info/github/bbatsov/powerpack/Enumerable#average-instance_method)
28
29
  * [#drop_last](http://rdoc.info/github/bbatsov/powerpack/Enumerable#drop_last-instance_method)
29
30
  * [#drop_last_while](http://rdoc.info/github/bbatsov/powerpack/Enumerable#drop_last_while-instance_method)
31
+ * [#exactly?](http://rdoc.info/github/bbatsov/powerpack/Enumerable#exactly?-instance_method)
30
32
  * [#frequencies](http://rdoc.info/github/bbatsov/powerpack/Enumerable#frequencies-instance_method)
31
33
  * [#several?](http://rdoc.info/github/bbatsov/powerpack/Enumerable#several?-instance_method)
32
34
  * [#sum](http://rdoc.info/github/bbatsov/powerpack/Enumerable#sum-instance_method)
@@ -37,6 +39,12 @@ Or install it yourself as:
37
39
  * [Numeric](http://rdoc.info/github/bbatsov/powerpack/Numeric)
38
40
  * [#pos?](http://rdoc.info/github/bbatsov/powerpack/Numeric#pos?-instance_method)
39
41
  * [#neg?](http://rdoc.info/github/bbatsov/powerpack/Numeric#neg?-instance_method)
42
+ * [#hundred](http://rdoc.info/github/bbatsov/powerpack/Numeric#hundred-instance_method)
43
+ * [#thousand](http://rdoc.info/github/bbatsov/powerpack/Numeric#thousand-instance_method)
44
+ * [#million](http://rdoc.info/github/bbatsov/powerpack/Numeric#million-instance_method)
45
+ * [#billion](http://rdoc.info/github/bbatsov/powerpack/Numeric#billion-instance_method)
46
+ * [#trillion](http://rdoc.info/github/bbatsov/powerpack/Numeric#trillion-instance_method)
47
+ * [#quadrillion](http://rdoc.info/github/bbatsov/powerpack/Numeric#quadrillion-instance_method)
40
48
  * [String](http://rdoc.info/github/bbatsov/powerpack/String)
41
49
  * [#blank?](http://rdoc.info/github/bbatsov/powerpack/String#blank?-instance_method)
42
50
  * [#format](http://rdoc.info/github/bbatsov/powerpack/String#format-instance_method)
@@ -70,6 +78,10 @@ To load only a specific extension like `String#format` do:
70
78
  require 'powerpack/string/format'
71
79
  ```
72
80
 
81
+ ## Changelog
82
+
83
+ Powerpack's changelog is available [here](CHANGELOG.md).
84
+
73
85
  ## Contributing
74
86
 
75
87
  1. Fork it
@@ -1,2 +1,2 @@
1
1
  require_relative 'array/butlast'
2
- require_relative 'array/tail'
2
+ require_relative 'array/butfirst'
@@ -1,4 +1,4 @@
1
- unless Array.method_defined? :tail
1
+ unless Array.method_defined? :butfirst
2
2
  class Array
3
3
  # Returns a new array rejecting the current one's first element.
4
4
  #
@@ -6,9 +6,9 @@ unless Array.method_defined? :tail
6
6
  # array is empty
7
7
  #
8
8
  # @example
9
- # [1, 2, 3].tail #=> [2, 3]
10
- # [].tail #=> nil
11
- def tail
9
+ # [1, 2, 3].butfirst #=> [2, 3]
10
+ # [].butfirst #=> nil
11
+ def butfirst
12
12
  self[1..-1]
13
13
  end
14
14
  end
@@ -1,5 +1,7 @@
1
+ require_relative 'enumerable/average'
1
2
  require_relative 'enumerable/drop_last'
2
3
  require_relative 'enumerable/drop_last_while'
4
+ require_relative 'enumerable/exactly'
3
5
  require_relative 'enumerable/frequencies'
4
6
  require_relative 'enumerable/several'
5
7
  require_relative 'enumerable/sum'
@@ -0,0 +1,20 @@
1
+ unless Enumerable.method_defined? :average
2
+ module Enumerable
3
+ # Calculates the average of a numeric collection.
4
+ #
5
+ # @param default [Object] an optional default return value if there are no elements.
6
+ # It's nil by default.
7
+ # @return The average of the elements or the default value if there are no
8
+ # elements.
9
+ #
10
+ # @example
11
+ # [1, 2, 3].average #=> 2
12
+ # [1, 2, 3, 4].average #=> 2.5
13
+ # [].average #=> nil
14
+ # [].average(0) #=> 0
15
+ def average(default = nil)
16
+ coll_size = to_a.size
17
+ coll_size > 0 ? reduce(&:+) / coll_size.to_f : default
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ unless Enumerable.method_defined? :exactly?
2
+ module Enumerable
3
+ # Checks if exactly n elements meet a certain predicate.
4
+ #
5
+ # @param n [Fixnum] the number of matches required
6
+ # @return [Boolean] true if we get exactly n matches, false otherwise
7
+ #
8
+ # @example
9
+ # [1, 2, 3, 4].exactly?(1) { |n| n > 3 } #=> true
10
+ # [1, 2, 3, 4].exactly?(2, &:even?) #=> true
11
+ # [1, 1, 3, 3].exactly?(2, &:even?) #=> false
12
+ #
13
+ # Without a block uses the identify of the elements as default predicate.
14
+ # This means that nil and false elements will be ignored.
15
+ #
16
+ # @example
17
+ # [1, false, nil].exactly?(3) #=> false
18
+ # [1, false, nil].exactly?(1) #=> true
19
+ # [false, nil].exactly?(0) #=> true
20
+ # [1, 2, 3].exactly?(3) #=>true
21
+ def exactly?(n)
22
+ found_count = 0
23
+
24
+ if block_given?
25
+ each do |*o|
26
+ if yield(*o)
27
+ found_count += 1
28
+ end
29
+ end
30
+ else
31
+ each do |o|
32
+ if o
33
+ found_count += 1
34
+ end
35
+ end
36
+ end
37
+
38
+ n == found_count
39
+ end
40
+ end
41
+ end
@@ -5,16 +5,12 @@ unless Enumerable.method_defined? :take_last_while?
5
5
  # @return [Array] an array containing the matching elements
6
6
  #
7
7
  # @example
8
- # [1, 2, 3, 5].take_last_while(&:odd?) #=> [5, 3]
8
+ # [1, 2, 3, 5].take_last_while(&:odd?) #=> [3, 5]
9
9
  def take_last_while
10
10
  return to_enum(:take_last_while) unless block_given?
11
11
 
12
12
  result = []
13
- reverse_each do |elem|
14
- return result unless yield(elem)
15
- result << elem
16
- end
17
-
13
+ reverse_each { |elem| yield(elem) ? result.unshift(elem) : break }
18
14
  result
19
15
  end
20
16
  end
@@ -2,10 +2,11 @@ unless Hash.method_defined? :symbolize_keys
2
2
  class Hash
3
3
  # Converts the keys of the hash to symbols.
4
4
  #
5
- # @return [Hash] a copy of the original hash with its keys converted to symbols
5
+ # @return [Hash] a copy of the original hash with its keys converted to symbols. Leave nonconvertible keys untouched when symbolizing keys.
6
6
  #
7
7
  # @example
8
8
  # { 'one' => 1, 'two' => 2 }.symbolize_keys #=> { :one => 1, :two => 2 }
9
+ # { 1 => "a" }.symbolize_keys #=> { 1 => "a" }
9
10
  def symbolize_keys
10
11
  Hash[map { |(k, v)| [(k.to_sym rescue k), v] }]
11
12
  end
@@ -1,2 +1,3 @@
1
1
  require_relative 'numeric/neg'
2
2
  require_relative 'numeric/pos'
3
+ require_relative 'numeric/scale'
@@ -0,0 +1,44 @@
1
+ class Numeric
2
+ HUNDRED = 10 ** 2
3
+ THOUSAND = 10 ** 3
4
+ MILLION = 10 ** 6
5
+ BILLION = 10 ** 9
6
+ TRILLION = 10 ** 12
7
+ QUADRILLION = 10 ** 15
8
+
9
+ unless Numeric.method_defined? :hundred
10
+ def hundred
11
+ self * HUNDRED
12
+ end
13
+ end
14
+
15
+ unless Numeric.method_defined? :thousand
16
+ def thousand
17
+ self * THOUSAND
18
+ end
19
+ end
20
+
21
+ unless Numeric.method_defined? :million
22
+ def million
23
+ self * MILLION
24
+ end
25
+ end
26
+
27
+ unless Numeric.method_defined? :billion
28
+ def billion
29
+ self * BILLION
30
+ end
31
+ end
32
+
33
+ unless Numeric.method_defined? :trillion
34
+ def trillion
35
+ self * TRILLION
36
+ end
37
+ end
38
+
39
+ unless Numeric.method_defined? :quadrillion
40
+ def quadrillion
41
+ self * QUADRILLION
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Powerpack
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Array#tail' do
3
+ describe 'Array#butfirst' do
4
4
  it 'returns an array without the first element' do
5
- expect([1, 2, 3].tail).to eq([2, 3])
5
+ expect([1, 2, 3].butfirst).to eq([2, 3])
6
6
  end
7
7
 
8
8
  it 'returns nil for empty lists' do
9
- expect([].tail).to be_nil
9
+ expect([].butfirst).to be_nil
10
10
  end
11
11
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Enumerable#average' do
4
+ it 'calculates the average of an enum' do
5
+ expect((1..3).average).to eq(2)
6
+ end
7
+
8
+ it 'calculates the average of an array' do
9
+ expect([1, 2, 3, 4].average).to eq(2.5)
10
+ end
11
+
12
+ it 'returns nil when invoked on an empty collection' do
13
+ expect([].average).to be_nil
14
+ end
15
+
16
+ it 'returns default value when invoked on an empty collection' do
17
+ expect([].average(0)).to be_zero
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Enumerable#exactly' do
4
+ context 'with block' do
5
+ it 'returns true for exact number of matches' do
6
+ expect([1, 2, 3, 4].exactly?(2, &:even?)).to be_true
7
+ end
8
+
9
+ it 'returns false for less matches' do
10
+ expect([1, 3, 4].exactly?(2, &:even?)).to be_false
11
+ end
12
+
13
+ it 'returns false for more matches' do
14
+ expect([1, 3, 4, 6, 8].exactly?(2, &:even?)).to be_false
15
+ end
16
+ end
17
+
18
+ context 'without block' do
19
+ it 'returns true for exact number of non nil/false elements in absence of nil/false elements' do
20
+ expect([1, 2, 3, 4].exactly?(4)).to be_true
21
+ end
22
+
23
+ it 'returns true for exact number of non nil/false elements in presence of nil/false elements' do
24
+ expect([1, 2, nil, false].exactly?(2)).to be_true
25
+ end
26
+
27
+ it 'returns true for exact number of nil/false elements' do
28
+ expect([nil, false].exactly?(0)).to be_true
29
+ end
30
+
31
+ it 'returns false if there are less non nil/false elements in absence of nil/false elements' do
32
+ expect([1, 2, 3].exactly?(4)).to be_false
33
+ end
34
+
35
+ it 'returns false if there are less non nil/false elements in presence of nil/false elements' do
36
+ expect([1, nil, false].exactly?(4)).to be_false
37
+ end
38
+ end
39
+ end
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe 'Enumerable#take_last_while' do
4
4
  it 'takes the last elements while a pred is true' do
5
- expect([1, 2, 3, 4, 6].take_last_while(&:even?)).to eq([6, 4])
5
+ expect([1, 2, 3, 4, 6].take_last_while(&:even?)).to eq([4, 6])
6
6
  end
7
7
  end
@@ -0,0 +1,139 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Numeric#hundred' do
4
+ it 'returns positive integer for positive integer' do
5
+ expect(1.hundred).to eql 100
6
+ end
7
+
8
+ it 'returns positive float for positive float' do
9
+ expect(0.1.hundred).to eql 10.0
10
+ end
11
+
12
+ it 'returns false for negative integer' do
13
+ expect(-1.hundred).to eql -100
14
+ end
15
+
16
+ it 'returns false for negative float' do
17
+ expect(-0.1.hundred).to eql -10.0
18
+ end
19
+
20
+ it 'returns 0 for 0' do
21
+ expect(0.hundred).to eql 0
22
+ expect(0.0.hundred).to eql 0.0
23
+ end
24
+ end
25
+
26
+ describe 'Numeric#thousand' do
27
+ it 'returns positive integer for positive integer' do
28
+ expect(1.thousand).to eql 1000
29
+ end
30
+
31
+ it 'returns positive float for positive float' do
32
+ expect(0.1.thousand).to eql 100.0
33
+ end
34
+
35
+ it 'returns false for negative integer' do
36
+ expect(-1.thousand).to eql -1000
37
+ end
38
+
39
+ it 'returns false for negative float' do
40
+ expect(-0.1.thousand).to eql -100.0
41
+ end
42
+
43
+ it 'returns 0 for 0' do
44
+ expect(0.thousand).to eql 0
45
+ expect(0.0.thousand).to eql 0.0
46
+ end
47
+ end
48
+
49
+ describe 'Numeric#million' do
50
+ it 'returns positive integer for positive integer' do
51
+ expect(1.million).to eql 1000000
52
+ end
53
+
54
+ it 'returns positive float for positive float' do
55
+ expect(0.1.million).to eql 100000.0
56
+ end
57
+
58
+ it 'returns false for negative integer' do
59
+ expect(-1.million).to eql -1000000
60
+ end
61
+
62
+ it 'returns false for negative float' do
63
+ expect(-0.1.million).to eql -100000.0
64
+ end
65
+
66
+ it 'returns 0 for 0' do
67
+ expect(0.million).to eql 0
68
+ expect(0.0.million).to eql 0.0
69
+ end
70
+ end
71
+
72
+ describe 'Numeric#billion' do
73
+ it 'returns positive integer for positive integer' do
74
+ expect(1.billion).to eql 1000000000
75
+ end
76
+
77
+ it 'returns positive float for positive float' do
78
+ expect(0.1.billion).to eql 100000000.0
79
+ end
80
+
81
+ it 'returns false for negative integer' do
82
+ expect(-1.billion).to eql -1000000000
83
+ end
84
+
85
+ it 'returns false for negative float' do
86
+ expect(-0.1.billion).to eql -100000000.0
87
+ end
88
+
89
+ it 'returns 0 for 0' do
90
+ expect(0.billion).to eql 0
91
+ expect(0.0.billion).to eql 0.0
92
+ end
93
+ end
94
+
95
+ describe 'Numeric#trillion' do
96
+ it 'returns positive integer for positive integer' do
97
+ expect(1.trillion).to eql 1000000000000
98
+ end
99
+
100
+ it 'returns positive float for positive float' do
101
+ expect(0.1.trillion).to eql 100000000000.0
102
+ end
103
+
104
+ it 'returns false for negative integer' do
105
+ expect(-1.trillion).to eql -1000000000000
106
+ end
107
+
108
+ it 'returns false for negative float' do
109
+ expect(-0.1.trillion).to eql -100000000000.0
110
+ end
111
+
112
+ it 'returns 0 for 0' do
113
+ expect(0.trillion).to eql 0
114
+ expect(0.0.trillion).to eql 0.0
115
+ end
116
+ end
117
+
118
+ describe 'Numeric#quadrillion' do
119
+ it 'returns positive integer for positive integer' do
120
+ expect(1.quadrillion).to eql 1000000000000000
121
+ end
122
+
123
+ it 'returns positive float for positive float' do
124
+ expect(0.1.quadrillion).to eql 100000000000000.0
125
+ end
126
+
127
+ it 'returns false for negative integer' do
128
+ expect(-1.quadrillion).to eql -1000000000000000
129
+ end
130
+
131
+ it 'returns false for negative float' do
132
+ expect(-0.1.quadrillion).to eql -100000000000000.0
133
+ end
134
+
135
+ it 'returns 0 for 0' do
136
+ expect(0.quadrillion).to eql 0
137
+ expect(0.0.quadrillion).to eql 0.0
138
+ end
139
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: powerpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-03 00:00:00.000000000 Z
11
+ date: 2013-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -76,17 +76,20 @@ files:
76
76
  - .gitignore
77
77
  - .rspec
78
78
  - .travis.yml
79
+ - CHANGELOG.md
79
80
  - Gemfile
80
81
  - LICENSE.txt
81
82
  - README.md
82
83
  - Rakefile
83
84
  - lib/powerpack.rb
84
85
  - lib/powerpack/array.rb
86
+ - lib/powerpack/array/butfirst.rb
85
87
  - lib/powerpack/array/butlast.rb
86
- - lib/powerpack/array/tail.rb
87
88
  - lib/powerpack/enumerable.rb
89
+ - lib/powerpack/enumerable/average.rb
88
90
  - lib/powerpack/enumerable/drop_last.rb
89
91
  - lib/powerpack/enumerable/drop_last_while.rb
92
+ - lib/powerpack/enumerable/exactly.rb
90
93
  - lib/powerpack/enumerable/frequencies.rb
91
94
  - lib/powerpack/enumerable/several.rb
92
95
  - lib/powerpack/enumerable/sum.rb
@@ -97,6 +100,7 @@ files:
97
100
  - lib/powerpack/numeric.rb
98
101
  - lib/powerpack/numeric/neg.rb
99
102
  - lib/powerpack/numeric/pos.rb
103
+ - lib/powerpack/numeric/scale.rb
100
104
  - lib/powerpack/string.rb
101
105
  - lib/powerpack/string/blank.rb
102
106
  - lib/powerpack/string/format.rb
@@ -105,10 +109,12 @@ files:
105
109
  - lib/powerpack/string/strip_margin.rb
106
110
  - lib/powerpack/version.rb
107
111
  - powerpack.gemspec
112
+ - spec/powerpack/array/butfirst_spec.rb
108
113
  - spec/powerpack/array/butlast_spec.rb
109
- - spec/powerpack/array/tail_spec.rb
114
+ - spec/powerpack/enumerable/average_spec.rb
110
115
  - spec/powerpack/enumerable/drop_last_spec.rb
111
116
  - spec/powerpack/enumerable/drop_last_while_spec.rb
117
+ - spec/powerpack/enumerable/exactly_spec.rb
112
118
  - spec/powerpack/enumerable/frequencies_spec.rb
113
119
  - spec/powerpack/enumerable/several_spec.rb
114
120
  - spec/powerpack/enumerable/sum_spec.rb
@@ -117,6 +123,7 @@ files:
117
123
  - spec/powerpack/hash/symbolize_keys_spec.rb
118
124
  - spec/powerpack/numeric/neg_spec.rb
119
125
  - spec/powerpack/numeric/pos_spec.rb
126
+ - spec/powerpack/numeric/scale_spec.rb
120
127
  - spec/powerpack/string/blank_spec.rb
121
128
  - spec/powerpack/string/format_spec.rb
122
129
  - spec/powerpack/string/squish_spec.rb
@@ -148,10 +155,12 @@ signing_key:
148
155
  specification_version: 4
149
156
  summary: A few useful extensions to core Ruby classes.
150
157
  test_files:
158
+ - spec/powerpack/array/butfirst_spec.rb
151
159
  - spec/powerpack/array/butlast_spec.rb
152
- - spec/powerpack/array/tail_spec.rb
160
+ - spec/powerpack/enumerable/average_spec.rb
153
161
  - spec/powerpack/enumerable/drop_last_spec.rb
154
162
  - spec/powerpack/enumerable/drop_last_while_spec.rb
163
+ - spec/powerpack/enumerable/exactly_spec.rb
155
164
  - spec/powerpack/enumerable/frequencies_spec.rb
156
165
  - spec/powerpack/enumerable/several_spec.rb
157
166
  - spec/powerpack/enumerable/sum_spec.rb
@@ -160,6 +169,7 @@ test_files:
160
169
  - spec/powerpack/hash/symbolize_keys_spec.rb
161
170
  - spec/powerpack/numeric/neg_spec.rb
162
171
  - spec/powerpack/numeric/pos_spec.rb
172
+ - spec/powerpack/numeric/scale_spec.rb
163
173
  - spec/powerpack/string/blank_spec.rb
164
174
  - spec/powerpack/string/format_spec.rb
165
175
  - spec/powerpack/string/squish_spec.rb