ruby-beautify 0.93.2 → 0.94.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55536d953a284d9c806cc5bf574a58b47c9dfa0c
4
- data.tar.gz: 9f1a5fc6b9521790f1de0bfa73bfcc2689a30cc0
3
+ metadata.gz: 894e285bf45a3569ff950d814687a9df9fd9ac1f
4
+ data.tar.gz: 913f62f88fd4e65783adcc71f2193fc3ee65bf1d
5
5
  SHA512:
6
- metadata.gz: 35a6084fb1a1d779fb7df49cfc476da0bb71561258a9f69aba81c7e4de3f3758dfb62fe0a1bb51ae8ea5bd0f4b75409731bdd0f2f58c620d26c40986424aea4c
7
- data.tar.gz: 1e547ca038a2c4552e1cf52f1b802e23b0c8e1a9709f1402792607a9950f514ebfa409d5ffbb99ead1e179c9c7f8396c13fbb641a90561370db5778e1fed5f11
6
+ metadata.gz: 0769f3ed536d176898707c9fdbd2f46c26ec8a84c84be60f81e01a90b72a78324f4aa81f257e90ba3300eebc5b355ffbf130311983d6c5aff74aea7c993e6871
7
+ data.tar.gz: f5ee63bf346accda19c6a5337003e0775c3c06d583099b797ef38e57d24901d5f1ba194b7c756176d71b726152b352ecc9a591b4e251160964c7fa94b9ca7ba6
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.0.0"
4
+ - "2.1.0"
5
+ branches:
6
+ only:
7
+ - master
8
+ - dev
9
+ script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -2,3 +2,11 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in ruby-beautify.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ gem 'guard', require:false
8
+ gem 'guard-rspec', require:false
9
+ gem 'guard-bundler', require:false
10
+ gem 'pry', require:false
11
+ gem "codeclimate-test-reporter", group: :test, require: nil
12
+ end
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ guard :bundler do
2
+ watch('Gemfile')
3
+ watch(/^.+\.gemspec/)
4
+ end
5
+
6
+ guard :rspec, cmd: 'rspec' do
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
9
+ watch('spec/spec_helper.rb') { "spec" }
10
+ end
data/README.md CHANGED
@@ -1,66 +1,70 @@
1
- # RBeautify
1
+ # Ruby Beautify
2
+ **Master** [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/erniebrodeur/ruby-beautify) [![Build Status](https://travis-ci.org/erniebrodeur/ruby-beautify.png?branch=master)](https://travis-ci.org/erniebrodeur/ruby-beautify) [![Dependency Status](https://gemnasium.com/erniebrodeur/ruby-beautify.png)](https://gemnasium.com/erniebrodeur/ruby-beautify)
3
+ **Development** [![Build Status](https://travis-ci.org/erniebrodeur/ruby-beautify.png?branch=development)](https://travis-ci.org/erniebrodeur/ruby-beautify)
4
+
2
5
  [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/erniebrodeur/ruby-beautify?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3
6
 
4
- This gem provides a cli binary named 'rbeautify' that will pretty up ruby code.
7
+ This gem provides a cli binary named 'ruby-beautify' that will pretty up ruby code.
5
8
 
9
+ Currenty, 'rbeautify' is included for backwards compatibility but will likely be phased out at some point.
6
10
 
7
11
  ## Installation
8
12
 
9
- % gem install ruby-beautify
13
+ % gem install ruby-beautify
10
14
 
11
15
  ## Usage
12
16
 
13
17
  To Pretty up a file:
14
18
 
15
- % rbeautify filename
19
+ % ruby-beautify filename
16
20
 
17
21
  Without a filename it reads from STDIN, suitable for piping:
18
22
 
19
- % curl 'http://example.org/ugly-file.rb' | rbeautify
23
+ % curl 'http://example.org/ugly-file.rb' | ruby-beautify
20
24
 
21
25
  It has help:
22
26
 
23
- % rbeautify --help
27
+ % ruby-beautify --help
24
28
 
25
29
  You can pick your indent character:
26
30
 
27
- % rbeautify --(t)abs
31
+ % ruby-beautify --(t)abs
28
32
 
29
- % rbeautify --(s)paces
33
+ % ruby-beautify --(s)paces
30
34
 
31
35
  You can also pick the count of characters:
32
36
 
33
- % rbeautify --indent_(c)ount 1
37
+ % ruby-beautify --indent_(c)ount 1
34
38
 
35
39
  Examples:
36
40
 
37
- % rbeautify -c 2 -s filename
41
+ % ruby-beautify -c 2 -s filename
38
42
 
39
- % rbeautify filename
43
+ % ruby-beautify filename
40
44
 
41
- % rbeautify -t -c 2 filename
45
+ % ruby-beautify -t -c 2 filename
42
46
 
43
47
  ## Bugs
44
48
 
45
- I don't have every ruby style tested against, so I expect some gaps. Feel free to submit issues (please include an example) and I'll figure out how to 'beautify' it.
49
+ I don't have every ruby style tested against, so I expect some gaps. Feel free to submit issues (please include an example) and I'll figure out how to 'beautify' it.
46
50
 
47
- Please feel free to open issues, I am actively working on this project again, thanks entirely to the ripper gem.
51
+ Please feel free to open issues, I am actively working on this project again, thanks entirely to the ripper gem.
48
52
 
49
53
  ## Todo
50
54
 
51
- * Add vim style comment hinting.
52
- * Add overwrite in place to files.
53
- * Add 'best guest' for files that fail syntax checking.
54
- * Add syntax checking to files rendered via STDIN.
55
+ * Add vim style comment hinting.
56
+ * Add overwrite in place to files.
57
+ * Add 'best guest' for files that fail syntax checking.
58
+ * Add syntax checking to files rendered via STDIN.
55
59
 
56
60
  Longer term I'd like to do some more to assignment, line wrapping, and spacing in/around keywords.
57
61
 
58
62
  ## Contributing
59
63
 
60
- * fork it.
61
- * create it.
62
- * push it.
63
- * pull it.
64
+ * fork it.
65
+ * create it.
66
+ * push it.
67
+ * pull it.
64
68
 
65
69
  # History
66
70
 
@@ -1,18 +1,23 @@
1
- # Release Notes
2
- #### 0.93.1
1
+ ## 0.94.0
2
+ * Added a very basic spec.
3
+ * Added better support for piping and redirection (thanks @veelenga)
4
+ * Renamed the bin to ruby-beautify and added a link for backwards compat.
5
+ * Fixed extra spaces on blank lines (thanks @veelenga)
6
+
7
+ ## 0.93.2
3
8
  * Fixed a typo in the help usage (thanks @ronald)
4
9
 
5
- #### 0.93.0
10
+ ## 0.93.0
6
11
  * Complete rewrite of the lexing engine.
7
12
 
8
- #### 0.92.0
13
+ ## 0.92.0
9
14
  * Renamed a require in rspec so they would work again.
10
15
  * Dropped filemagic since this already has excellent file parsing and is platform agnostic.
11
16
  * Dropped the rest of app.rb, since it wasn't adding anything but line count at this point.
12
17
  * Added a RELEASE.md to track changes as I go.
13
18
  * Dropped the unneeded yajl and sys-proctree deps since it doesn't use them.
14
19
 
15
- #### 0.91.0
20
+ ## 0.91.0
16
21
  * Stripped out the few bits of my cli stuff that I used and dropped app.rb, cli.rb and filemagic.rb, since all the functionality was present elsewhere.
17
22
  * Fixed up the gem related files, generating a proper gemspec, fixed the deps.
18
23
  * Fixed up the README.md to give a few usage examples and brief explanation.
data/bin/rbeautify CHANGED
@@ -38,8 +38,12 @@ def flatten_content(content)
38
38
  end
39
39
 
40
40
  def puts_indented_line(level, string)
41
- indent = (@indent_token * @indent_count) * level
42
- puts indent + string
41
+ if string.empty? or string =~ /^\n$/
42
+ puts
43
+ else
44
+ indent = (@indent_token * @indent_count) * level
45
+ puts indent + string
46
+ end
43
47
  end
44
48
 
45
49
  def pretty(content)
@@ -162,7 +166,7 @@ end
162
166
 
163
167
  # no argument, assume we want to open STDIN.
164
168
  if ARGV.empty?
165
- content = gets
169
+ content = $stdin.read
166
170
  puts pretty content
167
171
  else
168
172
  file = ARGV[0]
data/bin/ruby-beautify ADDED
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ruby-beautify'
3
+ require 'ripper'
4
+ require 'optparse'
5
+
6
+ Options = OptionParser.new do |opts|
7
+ opts.on("-V", "--version", "Print version") { |version| puts RBeautify::VERSION;exit 0}
8
+ opts.on("-c", "--indent_count [COUNT]", Integer, "Count of characters to use for indenting. (default: 1)") { |count| @indent_count = count}
9
+ opts.on("-t", "--tabs", "Use tabs for the indent character (default)") { @indent_token = "\t" }
10
+ opts.on("-s", "--spaces", "Use spaces for the indent character") { @indent_token = " " }
11
+ opts.banner = "Usage: print ruby into a pretty format, or break trying."
12
+ end
13
+ Options.parse!
14
+
15
+ @open_block_start = ["module", "class", "begin", "def", 'if', 'while', 'unless']
16
+ @both_block = ["else", "elsif", 'rescue']
17
+ @open_block_do = ['do', '{']
18
+ @close_block = ['end', '}']
19
+
20
+ @open_brackets = [:on_lparen, :on_lbracket, :on_lbrace]
21
+ @close_brackets = [:on_rparen, :on_rbracket, :on_rbrace]
22
+ @indent_token = "\t" unless @indent_token
23
+ @indent_count = 1 unless @indent_count
24
+
25
+ def flatten_content(content)
26
+ # I realize this is slow, todo, make it not slow.
27
+ flat_content = []
28
+ content.split("\n").each do |l|
29
+ w = l.split(/\s*(?<line>.*)/).last
30
+ if w
31
+ flat_content.push w
32
+ else
33
+ flat_content.push l
34
+ end
35
+ end
36
+
37
+ return flat_content.join("\n")
38
+ end
39
+
40
+ def puts_indented_line(level, string)
41
+ if string.empty? or string =~ /^\n$/
42
+ puts
43
+ else
44
+ indent = (@indent_token * @indent_count) * level
45
+ puts indent + string
46
+ end
47
+ end
48
+
49
+ def pretty(content)
50
+ # it's far easier if the content is 'flat', we don't have to worry about
51
+ # pre-existing indent levels. So lets lex that.
52
+ begin
53
+ lex = Ripper.lex(flatten_content(content))
54
+ rescue
55
+ exit 255
56
+ end
57
+
58
+ # quick way to get our total lines.
59
+ total_lines = lex.last[0][0]
60
+ line_index = 1
61
+
62
+ indent_level = 0
63
+
64
+ # walk through our lines.
65
+ while (line_index <= total_lines)
66
+ # a 'lex' copy of our line.
67
+ line_lex = lex.select {|l| l[0][0] == line_index}
68
+ # a string version.
69
+ line_string = line_lex.map {|l| l.last}.join("")
70
+
71
+ # did we just close something? if so, lets bring it down a level.
72
+ if closing_block?(line_lex) || closing_assignment?(line_lex)
73
+ indent_level -=1 if indent_level > 0
74
+ end
75
+
76
+ # print our line, in place.
77
+ puts_indented_line(indent_level, line_string)
78
+
79
+ # oh, we opened something did we? lets indent for the next run.
80
+ if opening_block?(line_lex) || opening_assignment?(line_lex)
81
+ indent_level +=1
82
+ end
83
+
84
+ line_index += 1
85
+ end
86
+
87
+ return nil
88
+ end
89
+
90
+ # how many times do we open in this line?
91
+ def opening_block_count(line_lex)
92
+ line_lex.select {|l| l[1] == :on_kw && @open_block_do.include?(l[2])}.count
93
+ end
94
+
95
+ # how many times do we close?
96
+ def closing_block_count(line_lex)
97
+ line_lex.select {|l| l[1] == :on_kw && @close_block.include?(l[2])}.count
98
+ end
99
+
100
+ # is the first word a key word?
101
+ def starts_block?(line_lex)
102
+ line_lex.first[1] == :on_kw && @open_block_start.include?(line_lex.first[2])
103
+ end
104
+
105
+ # is the first word one of our 'both' keywords?
106
+ def both_block?(line_lex)
107
+ return line_lex.first[1] == :on_kw && @both_block.include?(line_lex.first[2])
108
+ end
109
+
110
+ # kinda complex, we count open/close to determine if we ultimately have a
111
+ # hanging line. Always true if it's a both_block.
112
+ def opening_block?(line_lex)
113
+ return true if both_block? line_lex
114
+ opens = starts_block?(line_lex) ? 1 : 0
115
+ opens += opening_block_count line_lex
116
+ closes = closing_block_count line_lex
117
+ return false if opens == closes
118
+ return true if opens > closes
119
+ end
120
+
121
+ # kinda complex, we count open/close to determine if we ultimately have close a
122
+ # hanging line. Always true if it's a both_block.
123
+ def closing_block?(line_lex)
124
+ return true if both_block? line_lex
125
+ opens = starts_block?(line_lex) ? 1 : 0
126
+ opens += opening_block_count line_lex
127
+ closes = closing_block_count line_lex
128
+ return false if opens == closes
129
+ return true if opens < closes
130
+ end
131
+
132
+ # count the amount of opening assignments.
133
+ def opening_assignment_count(line_lex)
134
+ line_lex.select {|l| @open_brackets.include? l[1]}.count
135
+ end
136
+
137
+ # count the amount of closing assignments.
138
+ def closing_assignment_count(line_lex)
139
+ line_lex.select {|l| @close_brackets.include? l[1]}.count
140
+ end
141
+
142
+ # same trick as opening_block
143
+ def opening_assignment?(line_lex)
144
+ opens = opening_assignment_count line_lex
145
+ closes = closing_assignment_count line_lex
146
+ return false if opens == closes
147
+ return true if opens > closes
148
+ end
149
+
150
+ # ...
151
+ def closing_assignment?(line_lex)
152
+ opens = opening_assignment_count line_lex
153
+ closes = closing_assignment_count line_lex
154
+ return false if opens == closes
155
+ return true if closes > opens
156
+ end
157
+
158
+ # is this ruby file valid syntax?
159
+ def valid_syntax?(file)
160
+ #ugly but fast, ruby returns stdout if it's ok, and stderr if not.
161
+ #if we get anything back, we were ok, else we were not.
162
+ r = `ruby -c #{file} 2> /dev/null`
163
+ return false if r == ""
164
+ return true
165
+ end
166
+
167
+ # no argument, assume we want to open STDIN.
168
+ if ARGV.empty?
169
+ content = $stdin.read
170
+ puts pretty content
171
+ else
172
+ file = ARGV[0]
173
+ if File.exist? file
174
+ if valid_syntax? file
175
+ content = open(file).read
176
+ pretty content
177
+ else
178
+ puts content
179
+ exit 127
180
+ end
181
+ else
182
+ puts "No such file: #{file}"
183
+ end
184
+ end
@@ -1,3 +1,3 @@
1
1
  module RBeautify
2
- VERSION = "0.93.2"
2
+ VERSION = "0.94.0"
3
3
  end
@@ -8,7 +8,6 @@ Gem::Specification.new do |gem|
8
8
  gem.email = 'ebrodeur@ujami.net'
9
9
  gem.homepage = "https://github.com/erniebrodeur/ruby-beautify"
10
10
 
11
- gem.add_development_dependency "rspec"
12
11
  gem.files = `git ls-files`.split("\n")
13
12
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
13
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -16,4 +15,5 @@ Gem::Specification.new do |gem|
16
15
  gem.version = RBeautify::VERSION
17
16
  gem.add_development_dependency 'rake'
18
17
  gem.add_development_dependency 'bundler'
18
+ gem.add_development_dependency 'rspec'
19
19
  end
@@ -0,0 +1,22 @@
1
+ # one big spec (for now).
2
+ require 'spec_helper.rb'
3
+ require 'digest/md5'
4
+
5
+ # this is so simple, it's stupid. I basically used the 'example.rb' file while
6
+ # testing this indenting method. So I've visually approved of the output, and
7
+ # I've run that through md5. Then we just run our build, gather that md5 and
8
+ # compare the two.
9
+ # Eventually, I should break out the code into smaller chunks so I can identify
10
+ # which component is breaking down.
11
+
12
+ describe "Ruby Beautify" do
13
+ before (:all) do
14
+ @good_md5_sum = 'bd409d320cfe6fd0e2bf405e47cd2f2c'
15
+ @bad_file = 'spec/example.rb'
16
+ @bin = "#{Dir.pwd}/bin/ruby-beautify"
17
+ end
18
+ it 'will work' do
19
+ md5_sum = Digest::MD5.hexdigest `bundle exec #{@bin} #{@bad_file}`
20
+ expect(md5_sum).to eq @good_md5_sum
21
+ end
22
+ end
data/spec/example.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'ripper'
2
+
3
+ module Here
4
+ class There
5
+ def why?(argument = nil)
6
+ @array = [
7
+ 1,
8
+ 2,
9
+ 3
10
+ ]
11
+ hash = {
12
+ one:1,
13
+ two:2,
14
+ three:3
15
+ }
16
+ end
17
+
18
+ # a comment
19
+ def why_not?(argument: {})
20
+ @array = [4,5,6]
21
+ hash = {four:4, five:5, six:6}
22
+ s = "a #{"complex"} string."
23
+ end
24
+
25
+ def complex_method(one, two, three, four)
26
+ regex = /regex/
27
+ end
28
+
29
+ def with_block
30
+ run = Proc.new { |argument| puts arugment }
31
+ run do |arugment|
32
+ puts argument
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ h = Here::There.new
39
+
40
+ h.why?({
41
+ here: 'there',
42
+ there: 'here'
43
+ })
44
+
45
+ h.with_block {
46
+ puts 'yahooie'
47
+ }
48
+
49
+
50
+ h.complex_method('asdkjasflkjdglksjglksfgjlfkgjdf',
51
+ 'alfkjsdlkfjsflgkjfglk',
52
+ 'alfkjsdlkfjsflgkjfglk',
53
+ 'alfkjsdlkfjsflgkjfglk'
54
+ )
55
+
56
+ h.complex_method('asdkjasflkjdglksjglksfgjlfkgjdf',
57
+ 'alfkjsdlkfjsflgkjfglk',
58
+ 'alfkjsdlkfjsflgkjfglk',
59
+ 'alfkjsdlkfjsflgkjfglk'
60
+ ).map do |i|
61
+ i
62
+ end.map! { |i| i }
63
+
64
+ if 1 > 0
65
+ puts 'something'
66
+ elsif 1 < 0
67
+ puts 'never!'
68
+ else
69
+ puts 'not likely.'
70
+ end
@@ -0,0 +1,2 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
metadata CHANGED
@@ -1,74 +1,82 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-beautify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.93.2
4
+ version: 0.94.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ernie Brodeur
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-19 00:00:00.000000000 Z
11
+ date: 2014-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec
14
+ name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '>='
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: bundler
42
+ name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  description: a cli tool (and module) to beautify ruby code.
56
56
  email: ebrodeur@ujami.net
57
57
  executables:
58
58
  - rbeautify
59
+ - ruby-beautify
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
62
- - ".gitignore"
63
+ - .gitignore
64
+ - .rspec
65
+ - .travis.yml
63
66
  - Gemfile
67
+ - Guardfile
64
68
  - LICENSE
65
69
  - README.md
66
- - RELEASE.md
67
70
  - Rakefile
71
+ - WHATSNEW.md
68
72
  - bin/rbeautify
73
+ - bin/ruby-beautify
69
74
  - lib/ruby-beautify.rb
70
75
  - lib/ruby-beautify/version.rb
71
76
  - ruby-beautify.gemspec
77
+ - spec/bin/ruby-beautify_spec.rb
78
+ - spec/example.rb
79
+ - spec/spec_helper.rb
72
80
  homepage: https://github.com/erniebrodeur/ruby-beautify
73
81
  licenses: []
74
82
  metadata: {}
@@ -78,18 +86,21 @@ require_paths:
78
86
  - lib
79
87
  required_ruby_version: !ruby/object:Gem::Requirement
80
88
  requirements:
81
- - - ">="
89
+ - - '>='
82
90
  - !ruby/object:Gem::Version
83
91
  version: '0'
84
92
  required_rubygems_version: !ruby/object:Gem::Requirement
85
93
  requirements:
86
- - - ">="
94
+ - - '>='
87
95
  - !ruby/object:Gem::Version
88
96
  version: '0'
89
97
  requirements: []
90
98
  rubyforge_project:
91
- rubygems_version: 2.2.2
99
+ rubygems_version: 2.0.14
92
100
  signing_key:
93
101
  specification_version: 4
94
102
  summary: a cli tool (and module) to beautify ruby code.
95
- test_files: []
103
+ test_files:
104
+ - spec/bin/ruby-beautify_spec.rb
105
+ - spec/example.rb
106
+ - spec/spec_helper.rb