stockroll 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3dc6678fdbb06fe8356c3f8b9331ef29adb58f61
4
+ data.tar.gz: b692ef3c18b9f405a536f7096afac406de22eca1
5
+ SHA512:
6
+ metadata.gz: 251b4bfec0947e0c39b1859ff8ec078f1d5d74375fa0730aa308d8ad9f86d11c0372c3a783288faacec208d4e4fff7bd375ec014e7748c80590b888b1ef71a77
7
+ data.tar.gz: c54864d4be3db21c4cdc5549e82c2cbd6a21aeb6269b35834ff6c5e4d31cd03ff04174c619e2a063adeaddf546b5fd71a7dacd3ba844f77d10a639ea84d57505
@@ -0,0 +1,19 @@
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile ~/.gitignore_global
6
+
7
+ coverage
8
+ doc
9
+ pkg
10
+ *.swp
11
+ *.patch
12
+ *.gem
13
+ *.sh
14
+ .DS_Store
15
+ .bundle/*
16
+ #Gemfile.lock
17
+ tmp/*
18
+ log/*
19
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stockroll.gemspec
4
+ gemspec
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ stockroll (0.0.2)
5
+ colorize (~> 0.6.0)
6
+ curses (~> 1.0.0)
7
+ em-http-request (~> 1.1.2)
8
+ thor (~> 0.18.1)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ addressable (2.3.5)
14
+ colorize (0.6.0)
15
+ cookiejar (0.3.1)
16
+ curses (1.0.0)
17
+ diff-lcs (1.2.5)
18
+ em-http-request (1.1.2)
19
+ addressable (>= 2.3.4)
20
+ cookiejar
21
+ em-socksify (>= 0.3)
22
+ eventmachine (>= 1.0.3)
23
+ http_parser.rb (>= 0.6.0)
24
+ em-socksify (0.3.0)
25
+ eventmachine (>= 1.0.0.beta.4)
26
+ eventmachine (1.0.3)
27
+ http_parser.rb (0.6.0)
28
+ rake (10.1.1)
29
+ rspec (2.14.1)
30
+ rspec-core (~> 2.14.0)
31
+ rspec-expectations (~> 2.14.0)
32
+ rspec-mocks (~> 2.14.0)
33
+ rspec-core (2.14.7)
34
+ rspec-expectations (2.14.5)
35
+ diff-lcs (>= 1.1.3, < 2.0)
36
+ rspec-mocks (2.14.6)
37
+ thor (0.18.1)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bundler (~> 1.3)
44
+ rake
45
+ rspec
46
+ stockroll!
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Raymond Jiang
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,40 @@
1
+ # Stockroll
2
+ ### To show real-time stock quotes
3
+
4
+ ![screenshot](/screenshot.png)
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'stockroll'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install stockroll
19
+
20
+ ## Usage
21
+
22
+ 1. Very simple to use, only type 'stock' in your console and let's fun.
23
+
24
+ 2. Type 'stock --help' to view all commands.
25
+
26
+ 3. Type 'stock add your_stock_code_xxx' , add stock code to display.
27
+
28
+ 4. Type 'stock delete your_stock_code_xxx', delete a existed stock code.
29
+
30
+ 5. Type 'stock clear' to clear all existed stock codes.
31
+
32
+ 6. Type 'q' or 'Q' to quit stock.
33
+
34
+ ## Contributing
35
+
36
+ 1. Fork it
37
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
38
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
39
+ 4. Push to the branch (`git push origin my-new-feature`)
40
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'stockroll/commands'
3
+
4
+ Stockroll::Commands.start
@@ -0,0 +1,5 @@
1
+ require "stockroll/version"
2
+
3
+ module Stockroll
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,126 @@
1
+ require "thor"
2
+ require 'find'
3
+ require 'erb'
4
+ require 'colorize'
5
+ require 'stockroll/utils'
6
+
7
+ module Stockroll
8
+ class Commands < Thor
9
+ include Thor::Actions
10
+ CONF_FILE_PATH = File.expand_path("~/.stock_roll_codes_config",__FILE__)
11
+
12
+ desc "start", "start stock roll"
13
+ def start
14
+ codes = self.get_stock_codes
15
+ if codes.nil? or codes.strip.size == 0
16
+ isOk, cs = self.init_config
17
+ Stockroll::Utils.run(cs) if isOk
18
+ else
19
+ Stockroll::Utils.run(codes)
20
+ end
21
+ end
22
+
23
+ desc "add", "add a stock code, if you want add multiple CODE please separated by commas."
24
+ def add(codes)
25
+ is_valid , err_msg = self.check_codes(codes)
26
+ if is_valid
27
+ self.add_codes(codes)
28
+ puts "Info: add code successfully.".yellow
29
+ else
30
+ err_msg.each do |msg|
31
+ puts msg.red
32
+ end
33
+ end
34
+ end
35
+
36
+ desc "delete", "delete a stock code"
37
+ def delete(code)
38
+ self.remove_code(code)
39
+ puts "Info: delete is ok.".yellow
40
+ end
41
+
42
+ desc "clear", "clear all stock codes"
43
+ def clear()
44
+ self.remove_all_codes()
45
+ puts "Info: clear all codes is successed.".yellow
46
+ end
47
+
48
+ default_task :start
49
+
50
+ protected
51
+ def get_stock_codes
52
+ return nil unless File.exists?(CONF_FILE_PATH)
53
+ return IO.read(CONF_FILE_PATH)
54
+ end
55
+ def add_codes(codes)
56
+ raise 'Please input stock codes.' if codes.nil? or codes.strip.size == 0
57
+ codes = codes.gsub(/(^\s+)|(\s+$)/,'')
58
+ unless File.exists?(CONF_FILE_PATH)
59
+ File.open(CONF_FILE_PATH,'w') do |f|
60
+ f.write(codes)
61
+ end
62
+ else
63
+ content = self.get_stock_codes
64
+ if content.nil? or content.strip.size == 0
65
+ File.open(CONF_FILE_PATH,'w') do |f|
66
+ f.write(codes)
67
+ end
68
+ else
69
+ old_codes = content.split(',')
70
+ added_codes = codes.split(',')
71
+ new_codes = old_codes.concat(added_codes).uniq
72
+ File.open(CONF_FILE_PATH,'w') do |f|
73
+ f.write(new_codes.join(','))
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ def remove_code(code)
80
+ old_codes = self.get_stock_codes
81
+ unless old_codes.nil?
82
+ n = []
83
+ old_codes.split(',').each do |c|
84
+ if c.strip != code.strip
85
+ n << c
86
+ end
87
+ end
88
+ File.open(CONF_FILE_PATH,'w') do |f|
89
+ f.write(n.join(','))
90
+ end
91
+ end
92
+ end
93
+ def remove_all_codes()
94
+ File.open(CONF_FILE_PATH,'w') do |f|
95
+ f.write('')
96
+ end
97
+ end
98
+ def check_codes(codes)
99
+ is_valid = true
100
+ err_msg = []
101
+ c_a = codes.split(',')
102
+ c_a.each do |c|
103
+ unless Stockroll::Utils.is_valid_stock_code(c)
104
+ err_msg << "Error: invalid stock code: #{c}"
105
+ is_valid = false
106
+ end
107
+ end
108
+ [is_valid, err_msg]
109
+ end
110
+ def init_config
111
+ print "Please input your stock codes: ".yellow
112
+ cs = gets.chomp
113
+ is_valid, err_msg = self.check_codes(cs)
114
+ unless is_valid
115
+ err_msg.each do |msg|
116
+ puts msg.red
117
+ end
118
+ [false, '']
119
+ else
120
+ self.add_codes(cs)
121
+ [true, cs]
122
+ end
123
+ end
124
+ end
125
+
126
+ end
@@ -0,0 +1,144 @@
1
+
2
+ require 'net/http'
3
+ require 'colorize'
4
+ require 'em-http-request'
5
+ require "curses"
6
+
7
+ include Curses
8
+
9
+ module Stockroll
10
+ class Utils
11
+ ROOT_URL = "http://hq.sinajs.cn/list="
12
+ class << self
13
+ def is_valid_stock_code(code)
14
+ uri = URI("#{ROOT_URL}#{code}")
15
+ res = Net::HTTP.get(uri).force_encoding("GBK").encode("UTF-8")
16
+ val = res.split('=')[1]
17
+ if val =~ /([^\"\n;]+)/
18
+ if !$1.nil? and $1.strip.size > 0
19
+ true
20
+ else
21
+ false
22
+ end
23
+ else
24
+ false
25
+ end
26
+ end
27
+ def run(codes)
28
+ codes = codes.split(',')
29
+ Curses.noecho
30
+ Curses.init_screen
31
+ Curses.start_color
32
+ Curses.init_pair(COLOR_RED,COLOR_RED,COLOR_BLACK)
33
+ Curses.init_pair(COLOR_BLUE,COLOR_BLUE,COLOR_BLACK)
34
+ Curses.init_pair(COLOR_GREEN,COLOR_GREEN,COLOR_BLACK)
35
+ Curses.init_pair(COLOR_YELLOW,COLOR_YELLOW,COLOR_BLACK)
36
+ 47.times do |i|
37
+ Curses.setpos(0,i)
38
+ Curses.addstr('#')
39
+ end
40
+ curs_set(0)
41
+ Curses.setpos(1,0)
42
+ Curses.addstr('Loading...')
43
+
44
+ another_th = Thread.new {
45
+ EventMachine.run do
46
+
47
+ do_work = proc{
48
+
49
+ codes.each_with_index do |code,index|
50
+ http = EventMachine::HttpRequest.new("#{ROOT_URL}#{code}").get
51
+ http.errback { p 'Uh oh'; }
52
+ http.callback {
53
+ res = http.response.encode('utf-8')
54
+ val = res.split('=')[1]
55
+ if val =~ /([^\"\n;]+)/
56
+ captures = $1
57
+ if index == 0
58
+ ypos = 1
59
+ else
60
+ ypos = (index * 2) + 1
61
+ end
62
+
63
+ if captures.strip != ''
64
+ ar = captures.split(',')
65
+ Curses.setpos(ypos,0)
66
+ Curses.attron(A_BOLD|A_UNDERLINE) {
67
+ Curses.addstr(ar[0])
68
+ clrtoeol() #clear line
69
+ }
70
+
71
+ Curses.setpos(ypos,11)
72
+ Curses.attron(A_NORMAL|A_DIM) {
73
+ Curses.addstr(code)
74
+ }
75
+
76
+ Curses.setpos(ypos,22)
77
+ Curses.attron(A_NORMAL) {
78
+ Curses.addstr(ar[3])
79
+ }
80
+
81
+ Curses.setpos(ypos,32)
82
+ if ar[3].to_f == 0
83
+ diff = 0.00
84
+ else
85
+ diff = (ar[3].to_f - ar[2].to_f).round(2)
86
+ end
87
+ prefix = ' '
88
+ if diff >= 0
89
+ if diff == 0
90
+ Curses.attron(A_BOLD) {
91
+ Curses.addstr("#{prefix}#{diff.abs}")
92
+ Curses.addstr(" ")
93
+ Curses.addstr("0.00%")
94
+
95
+ }
96
+ else
97
+ prefix = '+'
98
+ Curses.attron(color_pair(COLOR_RED)|A_BOLD) {
99
+ Curses.addstr("#{prefix}#{diff.abs}")
100
+ Curses.addstr(" ")
101
+ Curses.addstr("#{prefix}#{((diff.abs/ar[2].to_f)*100).round(2)}%")
102
+
103
+ }
104
+ end
105
+ else
106
+ prefix = '-'
107
+ Curses.attron(color_pair(COLOR_GREEN)|A_BOLD) {
108
+ Curses.addstr("#{prefix}#{diff.abs}")
109
+ Curses.addstr(" ")
110
+ Curses.addstr("#{prefix}#{((diff.abs/ar[2].to_f)*100).round(2)}%")
111
+ }
112
+ end
113
+ Curses.refresh
114
+ end
115
+
116
+ end
117
+
118
+ }
119
+ end
120
+ }
121
+
122
+ EM::PeriodicTimer.new(10) do
123
+ do_work.call()
124
+ end
125
+ do_work.call()
126
+
127
+ end
128
+
129
+ }
130
+ while TRUE
131
+ c = getch
132
+ case c
133
+ when /q/i
134
+ EM.stop if EM
135
+ another_th.exit()
136
+ Curses.close_screen
137
+ exit(0)
138
+
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,3 @@
1
+ module Stockroll
2
+ VERSION = "0.0.2"
3
+ end
Binary file
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require "stockroll"
7
+
8
+
9
+ RSpec.configure do |config|
10
+ # Use color in STDOUT
11
+ config.color_enabled = true
12
+
13
+ # Use color not only in STDOUT but also in pagers and files
14
+ config.tty = true
15
+
16
+ # Use the specified formatter
17
+ config.formatter = :documentation # :progress, :html, :textmate
18
+ end
@@ -0,0 +1,36 @@
1
+ # run rspec
2
+ require "spec_helper"
3
+ require 'stockroll/commands'
4
+ require 'stockroll/utils'
5
+
6
+ describe Stockroll do
7
+
8
+ it 'stock code is valid' do
9
+ Stockroll::Utils.is_valid_stock_code('shabc').should == false
10
+ Stockroll::Utils.is_valid_stock_code('sz000100').should == true
11
+ end
12
+ describe "Stock Config Operation" do
13
+ path = Stockroll::Commands::CONF_FILE_PATH
14
+ before(:each) do
15
+ File.open(path,'w'){|f| f.write('')} if File.exists?(path)
16
+ end
17
+ it 'read and write stock code is fine' do
18
+ cmd = Stockroll::Commands.new
19
+ cmd.send :add_codes, "sz001"
20
+ cmd.send :add_codes, "sz001,sz002"
21
+ cmd.send :add_codes, "sz003"
22
+ (cmd.send :get_stock_codes).should == "sz001,sz002,sz003"
23
+ end
24
+ it 'remove stock code is fine' do
25
+ cmd = Stockroll::Commands.new
26
+ cmd.send :add_codes, "sz001"
27
+ cmd.send :add_codes, "sz001,sz002"
28
+ cmd.send :remove_code, "sz002"
29
+ (cmd.send :get_stock_codes).should == "sz001"
30
+ end
31
+ after(:each) do
32
+ File.open(path,'w'){|f| f.write('')} if File.exists?(path)
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stockroll/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "stockroll"
8
+ spec.version = Stockroll::VERSION
9
+ spec.authors = ["Robot Jiang"]
10
+ spec.email = ["robot.z.jiang@gmail.com"]
11
+ spec.description = %q{To show real-time stock quotes.}
12
+ spec.summary = %q{To show real-time stock quotes.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = ['stock']#spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_dependency "thor" , "~> 0.18.1"
25
+ spec.add_dependency "colorize" , "~> 0.6.0"
26
+ spec.add_dependency "em-http-request" , "~> 1.1.2"
27
+ spec.add_dependency "curses" , "~> 1.0.0"
28
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stockroll
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Robot Jiang
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.18.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.18.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.6.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.6.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: em-http-request
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.1.2
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.1.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: curses
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.0.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.0
111
+ description: To show real-time stock quotes.
112
+ email:
113
+ - robot.z.jiang@gmail.com
114
+ executables:
115
+ - stock
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - bin/stock
126
+ - lib/stockroll.rb
127
+ - lib/stockroll/commands.rb
128
+ - lib/stockroll/utils.rb
129
+ - lib/stockroll/version.rb
130
+ - screenshot.png
131
+ - spec/spec_helper.rb
132
+ - spec/stock_spec.rb
133
+ - stockroll.gemspec
134
+ homepage: ''
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.1.11
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: To show real-time stock quotes.
158
+ test_files:
159
+ - spec/spec_helper.rb
160
+ - spec/stock_spec.rb