fsxtrader 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ == 1.0.0 / 2007-08-21
2
+
3
+ * First ALPHA release of FSX Trader
4
+ * Seems to work fine on OS X, should work on *nix as well
5
+ * Added documentation / README
6
+ * Commented some of the tcode for a lil rdoc readability
7
+ * Tests _not_ added yet
8
+
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/fsxtrader
6
+ lib/fsxtrader.rb
7
+ lib/fsxtrader/application.rb
8
+ lib/fsxtrader/quote_parser.rb
9
+ lib/fsxtrader/trade.rb
10
+ lib/fsxtrader/trader.rb
11
+ test/test_fsxtrader.rb
@@ -0,0 +1,133 @@
1
+ fsxtrader
2
+ by Shanti A. Braford
3
+ http://shanti.railsblog.com/
4
+
5
+ == DESCRIPTION:
6
+
7
+ * FSX Trader allows you to automate trading on the Fantasy Stock Exchange app on Facebook
8
+ * Uses the WWW::Mechanize library (a ruby gem) to simulate a browser logging into Facebook, making trades, etc
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * Order types supported: buy, short, sell and cover
13
+ * Pulls real-time stock quotes from Yahoo! Finance
14
+
15
+ == SYNOPSIS:
16
+
17
+ Fantasy Stock Exchange app on Facebook (by HedgeStop.com):
18
+ http://apps.facebook.com/hedgestop/
19
+
20
+ As of this writing, the Fantasy Stock Exchange application on Facebook
21
+ does not allow one to place stop-limit orders or otherwise automatically
22
+ trigger a buy/sell or cover/short order based upon specified prices.
23
+
24
+ FSX Trader, when combined with a periodic cron job, can make these trades
25
+ for you.
26
+
27
+ Simply configure your trades in the '~/.fsx_trader.yml' file and they
28
+ will happen automatically whenever FSX Trader is run AND conditions for
29
+ making a trade (as specified via the config file) have been met.
30
+
31
+ Note: fsxtrader of course depends upon the external HTML form variables
32
+ of the FSX App and Facebook login page. If these variables have changed,
33
+ fsxtrader be temporarily broken until the gem can be updated.
34
+
35
+ A programmatic API into FSX would be ideal, but is currently not yet offered
36
+ by HedgeStop.
37
+
38
+ == REQUIREMENTS:
39
+
40
+ * Currently only works on OS X or Linux
41
+ * Other gems required: mechanize and cooloptions
42
+
43
+ == INSTALL:
44
+
45
+ * sudo gem install fsxtrader
46
+ * fsxtrader setup
47
+ * Now edit your '~/.fsx_config.yml' file, either by hand or using: fsxtrader edit
48
+ * Add a cron job to run fsx periodically on your system
49
+ * For more options, run: fsxtrader --help
50
+
51
+ == SAMPLE CRON:
52
+
53
+ Note: market hours are 9:30 AM to 4:00 PM EST
54
+
55
+ The following cron job would run fsxtrader every ten minutes
56
+ during the hours from 7 AM to 3PM (Sample West Coast config):
57
+
58
+ 0-59/10 7-15 * * * fsxtrader
59
+
60
+ Every five minutes from 9 AM to 4 PM (Sample East Coast config):
61
+
62
+ 0-59/5 9-16 * * * fsxtrader
63
+
64
+ If the above are giving you problems try doing a 'which fsxtrader' and using its
65
+ full path in the cron job or try:
66
+
67
+ 0-59/5 9-16 * * * `which fsxtrader` >> /path/to/a/fsxlog.txt
68
+
69
+ == SAMPLE CONFIG:
70
+
71
+ auth:
72
+ email: your_fb_email@gmail.com
73
+ pass: your_fb_password
74
+ trades:
75
+ # Format:
76
+ # prefix_key: symbol condition price action shares
77
+ #
78
+ # Options
79
+ # symbol - any valid NYSE or Nasdaq ticker tradeable on the FSX
80
+ # action - any of 'sell', 'cover', 'buy', 'short'
81
+ # condition - any of '>', '>=', '<', '<='
82
+ # shares - any integer between 1 and 500000
83
+ #
84
+ # Note: "prefix_key" must be a unique string, i.e. if you want to have two trade triggers
85
+ # in place for the same ticker symbol (WMT), you could do:
86
+ # wmt_long: WMT <= 18.5 buy 500
87
+ # wmt_short: WMT >= 57 short 1500
88
+ #
89
+ # The above config tells FSX Trader to do the following:
90
+ # * buy 500 shares of WMT (Wal-Mart Stores Inc) when its share price reaches 18.5 or lower
91
+ # * short 1500 shares of WMT (Wal-Mart Stores Inc) when its share prices reaches 57 or greater
92
+ #
93
+ #
94
+ # Configurations to automatically make NEW positions:
95
+ #
96
+ # Buy 20000 shares of CSCO if its price hits 15 dollars or lower:
97
+ buy_low_csco: CSCO <= 15 buy 20000
98
+ #
99
+ # Short 1700 shares of AAPL if its price hits 220:
100
+ short_appl: AAPL >= 220 short 1700
101
+ #
102
+ # Configurations to automatically get out of EXISTING positions:
103
+ #
104
+ # Sell 2000 shares of GOOG if its price hits 500:
105
+ goog: GOOG >= 500 sell 2000
106
+ #
107
+ # Cover 10000 shares of LEND if its price hits 1.75:
108
+ cover_lend: LEND <= 1.75 cover 10000
109
+
110
+ == LICENSE:
111
+
112
+ (The MIT License)
113
+
114
+ Copyright (c) 2007 Shanti A. Braford
115
+
116
+ Permission is hereby granted, free of charge, to any person obtaining
117
+ a copy of this software and associated documentation files (the
118
+ 'Software'), to deal in the Software without restriction, including
119
+ without limitation the rights to use, copy, modify, merge, publish,
120
+ distribute, sublicense, and/or sell copies of the Software, and to
121
+ permit persons to whom the Software is furnished to do so, subject to
122
+ the following conditions:
123
+
124
+ The above copyright notice and this permission notice shall be
125
+ included in all copies or substantial portions of the Software.
126
+
127
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
128
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
129
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
130
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
131
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
132
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
133
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/fsxtrader.rb'
6
+
7
+ Hoe.new('fsxtrader', Fsxtrader::VERSION) do |p|
8
+ p.rubyforge_name = 'fsxtrader'
9
+ p.author = 'Shanti A. Braford'
10
+ p.email = 'shantibraford@gmail.com'
11
+ p.summary = 'FSX Trader is a ruby client app that can be used to automatically make trades for you on the Fantasy Stock Exchange app on Facebook (powered by Hedgestop).'
12
+ p.description = p.paragraphs_of('README.txt', 2..6).join("\n\n")
13
+ # p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
+ p.remote_rdoc_dir = ''
15
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
+ p.extra_deps << ['mechanize', '>= 0.6.10']
17
+ p.extra_deps << ['cooloptions', '>= 1.1.1']
18
+ p.url = "http://fsxtrader.rubyforge.org"
19
+ end
20
+
21
+ desc "Release and publish documentation"
22
+ task :repubdoc => [:release, :publish_docs]
23
+
24
+ # vim: syntax=Ruby
@@ -0,0 +1,7 @@
1
+ begin
2
+ require 'fsxtrader'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'fsxtrader'
6
+ end
7
+ Fsxtrader.application.run
@@ -0,0 +1,37 @@
1
+ $:.unshift(File.dirname(__FILE__)+"/fsxtrader/")
2
+
3
+ # System gems
4
+ require 'rubygems'
5
+ require 'mechanize'
6
+ require 'open-uri'
7
+ require 'cooloptions'
8
+
9
+ # FSX Trader classes
10
+ require 'application'
11
+ require 'quote_parser'
12
+ require 'trader'
13
+ require 'trade'
14
+
15
+ class MissingQuoteError < StandardError; end
16
+
17
+ class Time
18
+ # Prints the date like: Monday, June 10 of 2006 at 10:57AM
19
+ def pretty_print
20
+ return self.strftime("%A, %B %d of %Y at %I:%M %p")
21
+ end
22
+ end
23
+
24
+ module Fsxtrader
25
+ VERSION = '1.0.0'
26
+
27
+ # Current Rake Application
28
+ def self.application
29
+ @application ||= Fsxtrader::Application.new
30
+ end
31
+
32
+ # Set the current Rake application object.
33
+ def self.application=(app)
34
+ @application = app
35
+ end
36
+ end
37
+
@@ -0,0 +1,163 @@
1
+ module Fsxtrader
2
+
3
+ class Application
4
+
5
+ # Initializes a new Fsxtrader::Application object.
6
+ #
7
+ # Note: this should always get called via a Singleton in the main
8
+ # Fsxtrader module as such: Fsxtrader.application.run
9
+ #
10
+ def initialize
11
+ end
12
+
13
+ # Main FSX Trader Application entry point.
14
+ # * Parses command-line options and calls methods accordingly
15
+ def run
16
+ @options = CoolOptions.parse!("[options] (setup|edit|help|run)") do |o|
17
+ o.desc help_txt
18
+ o.on "(v)erbose", "Turn on verbose mode", false
19
+ o.on "config-path PATH", "Path to config file", self.default_config_path
20
+
21
+ o.after do |r|
22
+ if (r.config_path == self.default_config_path) && !File.exist?(self.default_config_path)
23
+ setup
24
+ exit
25
+ end
26
+ unless File.exist?(r.config_path)
27
+ o.error("\nERROR: Invalid config file path: #{r.config_path}\nRun 'fsxtrader setup' to create the default config file.")
28
+ end
29
+ r.action = ARGV.shift || 'run'
30
+ end
31
+ end
32
+
33
+ case @options.action
34
+ when 'setup'
35
+ setup
36
+ when 'edit'
37
+ edit
38
+ when 'run'
39
+ go
40
+ when 'help'
41
+ help
42
+ else
43
+ help
44
+ end
45
+
46
+ end
47
+
48
+ # Displays the help text
49
+ def help
50
+ puts help_txt
51
+ end
52
+
53
+ # Creates the default config file in: ~/fsx_config.yml
54
+ def setup
55
+ File.open(self.default_config_path, 'w') do |f|
56
+ f.puts self.sample_config
57
+ end
58
+
59
+ puts '
60
+ Default config file has been created in:
61
+
62
+ ~/.fsx_config.yml
63
+
64
+ Edit this file with any text editor or run:
65
+
66
+ fsxtrader edit
67
+
68
+ Note: Be sure to edit your Facebook credentials in the config file!
69
+
70
+
71
+ To run FSX Trader and trigger actionable trades:
72
+
73
+ fsxtrader
74
+
75
+ '
76
+ end
77
+
78
+ # Edit the fsx trader config file (using the system editor)
79
+ def edit
80
+ puts "Editing FSX Trader..."
81
+ editor = ENV['editor'] || 'vi'
82
+ system "#{editor} #{self.default_config_path}"
83
+ end
84
+
85
+ # Runs the main FSX Trader application
86
+ # * loads config file and checks for trade triggers
87
+ # * pulls down any applicable stock quotes
88
+ # * makes trades if any trade conditions have been met
89
+ def go
90
+ puts "Running FSX Trader..."
91
+ trader = Fsxtrader::Trader.new(@options)
92
+ end
93
+
94
+ private
95
+
96
+ def default_config_path
97
+ home = ENV['HOME']
98
+ return File.join(home, '.fsx_config.yml')
99
+ end
100
+
101
+ def help_txt
102
+ '
103
+ To setup the initial config file, execute:
104
+
105
+ fsxtrader setup
106
+
107
+ Edit trades using your system edtiro:
108
+
109
+ fsxtrader edit
110
+
111
+ To check for actionable trades and execute them, simlpy run:
112
+
113
+ fsxtrader
114
+
115
+ '
116
+ end
117
+
118
+ def sample_config
119
+ 'auth:
120
+ email: your_fb_email@gmail.com
121
+ pass: your_fb_password
122
+ trades:
123
+ # Format:
124
+ # prefix_key: symbol condition price action shares
125
+ #
126
+ # Options
127
+ # symbol - any valid NYSE or Nasdaq ticker tradeable on the FSX
128
+ # action - any of \'sell\', \'cover\', \'buy\', \'short\'
129
+ # condition - any of \'>\', \'>=\', \'<\', \'<=\'
130
+ # shares - any integer between 1 and 500000
131
+ #
132
+ # Note: "prefix_key" must be a unique string. If you want to have two trade triggers
133
+ # in place for the same ticker symbol (WMT), you could do:
134
+ # wmt_long: WMT <= 18.5 buy 500
135
+ # wmt_short: WMT >= 57 short 1500
136
+ #
137
+ # The above config tells FSX Trader to do the following:
138
+ # * buy 500 shares of WMT (Wal-Mart Stores Inc) when its share price reaches 18.5 or lower
139
+ # * short 1500 shares of WMT (Wal-Mart Stores Inc) when its share prices reaches 57 or greater
140
+ #
141
+ #
142
+ # Configurations to automatically make NEW positions:
143
+ #
144
+ # Buy 20000 shares of CSCO if its price hits 15 dollars or lower:
145
+ # buy_low_csco: CSCO <= 15 buy 20000
146
+ #
147
+ # Short 1700 shares of AAPL if its price hits 220:
148
+ # short_appl: AAPL >= 220 short 1700
149
+ #
150
+ # Configurations to automatically get out of EXISTING positions:
151
+ #
152
+ # Sell 2000 shares of GOOG if its price hits 500:
153
+ # goog: GOOG >= 500 sell 2000
154
+ #
155
+ # Cover 10000 shares of LEND if its price hits 1.75:
156
+ # cover_lend: LEND <= 1.75 cover 10000
157
+
158
+ '
159
+ end
160
+
161
+ end
162
+
163
+ end
@@ -0,0 +1,20 @@
1
+ module Fsxtrader
2
+ class QuoteParser
3
+ attr_accessor :quotes
4
+ def initialize(symbols)
5
+ @url = "http://finance.yahoo.com/d/quotes.csv?s=#{symbols.join('+').upcase}&f=sl1"
6
+ @raw_csv = open(@url)
7
+ parse
8
+ end
9
+
10
+ def parse
11
+ @quotes = Hash.new
12
+ @raw_csv.each_line do |line|
13
+ #puts "Parsed Yahoo Stock Quote Line: #{line}"
14
+ line.gsub!('"', '')
15
+ symbol, price = *line.split(',')
16
+ @quotes[symbol] = price.to_f
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ module Fsxtrader
2
+ class Trade
3
+ attr_accessor :key, :symbol, :condition, :limit_price, :action, :shares
4
+ attr_accessor :trade_str
5
+ def initialize(key, trade_str)
6
+ # Expected params: "RDN >= 17.25 cover 110000"
7
+ # or : "IBM <= 85 sell 50000"
8
+ @key = key
9
+ @trade_str = trade_str
10
+ @symbol, @condition, @limit_price, @action, @shares = *trade_str.split
11
+ @shares = @shares.gsub(',', '') # strip commas
12
+ end
13
+ def to_s
14
+ "#{action} #{shares} of #{symbol} when it is #{condition} #{limit_price}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,152 @@
1
+ require 'fileutils'
2
+
3
+ module Fsxtrader
4
+
5
+ class Trader
6
+
7
+ # Initializes a new Fsxtrader::Trader class
8
+ # * Expects an OpenStruct returned from a CoolOptions.parse!(...) call
9
+ def initialize(options)
10
+ @agent = WWW::Mechanize.new
11
+ @agent.user_agent_alias = 'Mac FireFox'
12
+ @agent.redirect_ok = true
13
+
14
+ @config_file = options.config_path
15
+ @verbose = options.verbose || false
16
+
17
+ yml = File.open(@config_file)
18
+ @config = YAML.load(yml)
19
+ @auth = @config['auth']
20
+
21
+ return unless everything_ok?
22
+
23
+ @trades = []
24
+ @config['trades'].each do |key, val|
25
+ @trades << Trade.new(key, val)
26
+ end
27
+ if @trades.empty?
28
+ puts "No trades found."
29
+ return
30
+ else
31
+ puts "Trades Found:"
32
+ @trades.each do |t|
33
+ puts "\t#{t.key}: #{t.trade_str}"
34
+ end
35
+ end
36
+ symbols = @trades.collect { |trade| trade.symbol }
37
+ qp = QuoteParser.new(symbols)
38
+ @quotes = qp.quotes
39
+ check
40
+ end
41
+
42
+ # Check trades
43
+ def check
44
+ # First make sure we have a quote for each trade
45
+ quote_symbols = @quotes.keys
46
+ @trades.each do |trade|
47
+ raise MissingQuoteError, "for symbol: #{trade.symbol}" unless quote_symbols.include?(trade.symbol)
48
+ end
49
+ # Check prices
50
+ @trades.each do |trade|
51
+ puts "Trade: #{trade}" if @verbose
52
+ current_price = @quotes[trade.symbol]
53
+ puts "\tCurrent Price: #{current_price}" if @verbose
54
+ cond = "#{current_price} #{trade.condition} #{trade.limit_price}"
55
+ condition_met = eval(cond)
56
+ puts "\tCondition met?: #{condition_met}" if @verbose
57
+ if condition_met
58
+ make_trade(trade)
59
+ end
60
+ end
61
+ end
62
+
63
+ # Make a trade
64
+ def make_trade(trade)
65
+ login if !logged_in?
66
+
67
+ trade_type = 'old' if ['sell', 'cover'].include?(trade.action)
68
+ trade_type = 'new' if ['buy', 'short'].include?(trade.action)
69
+
70
+ trade_url = "http://apps.facebook.com/hedgestop/index.php?p=trade&type=#{trade_type}&ticker="
71
+
72
+ page = @agent.get(trade_url)
73
+ form = page.form('place_order')
74
+ b = form.buttons[1]
75
+
76
+ if @verbose
77
+ pp page
78
+ puts "\nForm: \n"
79
+ pp form
80
+ pp b
81
+ end
82
+
83
+ case trade_type
84
+ when 'old'
85
+ button = (trade.action == 'sell') ? form.buttons[0] : form.buttons[1]
86
+ when 'new'
87
+ button = (trade.action == 'buy') ? form.buttons[0] : form.buttons[1]
88
+ end
89
+ form.ticker = trade.symbol
90
+ form.shares = trade.shares
91
+ confirm_page = @agent.submit(form, button)
92
+ pp confirm_page if @verbose
93
+
94
+ confirm_form = confirm_page.form('confirm_order')
95
+ @agent.submit(confirm_form)
96
+
97
+ puts "Trade Executed: #{trade}"
98
+
99
+ # So that we don't execute this same trade again:
100
+ comment_out_trade(trade)
101
+ end
102
+
103
+ # Comments out a trade in the config file - this gets called after
104
+ # every successful trade (so that duplicate trades do not occur).
105
+ def comment_out_trade(trade)
106
+ config_txt = File.read(@config_file)
107
+ config_txt.gsub!("#{trade.key}:", "# Trade Executed on #{Time.now.pretty_print}:\n # #{trade.key}(executed):")
108
+ FileUtils.mv(@config_file, "#{@config_file}.bak")
109
+ # Now write out the new file
110
+ File.open(@config_file, 'w') do |f|
111
+ f.puts config_txt
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def everything_ok?
118
+ if 'your_fb_email@gmail.com' == @auth['email']
119
+ puts "\nPlease set your Facebook login credentials in the config file: ~/.fsx_config.yml\n\nOR using your system editor by running:\n\n fsxtrader edit\n\n"
120
+ return false
121
+ end
122
+
123
+ if @config['trades'].nil?
124
+ puts "No trades found."
125
+ return false
126
+ end
127
+
128
+ return true # everything's okay
129
+ end
130
+
131
+ def logged_in?
132
+ @logged_in
133
+ end
134
+
135
+ def login
136
+ page = @agent.get('http://www.facebook.com/')
137
+ pp page if @verbose
138
+
139
+ login_form = page.forms.first
140
+
141
+ login_form.email = @auth['email']
142
+ login_form.pass = @auth['pass']
143
+
144
+ pp login_form if @verbose
145
+
146
+ page = @agent.submit(login_form)
147
+ pp page if @verbose
148
+ @logged_in = true
149
+ end
150
+ end
151
+
152
+ end
File without changes
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: fsxtrader
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-08-21 00:00:00 -07:00
8
+ summary: FSX Trader is a ruby client app that can be used to automatically make trades for you on the Fantasy Stock Exchange app on Facebook (powered by Hedgestop).
9
+ require_paths:
10
+ - lib
11
+ email: shantibraford@gmail.com
12
+ homepage: http://fsxtrader.rubyforge.org
13
+ rubyforge_project: fsxtrader
14
+ description: "== FEATURES/PROBLEMS: * Order types supported: buy, short, sell and cover * Pulls real-time stock quotes from Yahoo! Finance == SYNOPSIS: Fantasy Stock Exchange app on Facebook (by HedgeStop.com): http://apps.facebook.com/hedgestop/ As of this writing, the Fantasy Stock Exchange application on Facebook does not allow one to place stop-limit orders or otherwise automatically trigger a buy/sell or cover/short order based upon specified prices. FSX Trader, when combined with a periodic cron job, can make these trades for you. Simply configure your trades in the '~/.fsx_trader.yml' file and they will happen automatically whenever FSX Trader is run AND conditions for making a trade (as specified via the config file) have been met. Note: fsxtrader of course depends upon the external HTML form variables of the FSX App and Facebook login page. If these variables have changed, fsxtrader be temporarily broken until the gem can be updated. A programmatic API into FSX would be ideal, but is currently not yet offered by HedgeStop. == REQUIREMENTS:"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Shanti A. Braford
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - bin/fsxtrader
37
+ - lib/fsxtrader.rb
38
+ - lib/fsxtrader/application.rb
39
+ - lib/fsxtrader/quote_parser.rb
40
+ - lib/fsxtrader/trade.rb
41
+ - lib/fsxtrader/trader.rb
42
+ - test/test_fsxtrader.rb
43
+ test_files:
44
+ - test/test_fsxtrader.rb
45
+ rdoc_options:
46
+ - --main
47
+ - README.txt
48
+ extra_rdoc_files:
49
+ - History.txt
50
+ - Manifest.txt
51
+ - README.txt
52
+ executables:
53
+ - fsxtrader
54
+ extensions: []
55
+
56
+ requirements: []
57
+
58
+ dependencies:
59
+ - !ruby/object:Gem::Dependency
60
+ name: mechanize
61
+ version_requirement:
62
+ version_requirements: !ruby/object:Gem::Version::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.6.10
67
+ version:
68
+ - !ruby/object:Gem::Dependency
69
+ name: cooloptions
70
+ version_requirement:
71
+ version_requirements: !ruby/object:Gem::Version::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.1.1
76
+ version:
77
+ - !ruby/object:Gem::Dependency
78
+ name: hoe
79
+ version_requirement:
80
+ version_requirements: !ruby/object:Gem::Version::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 1.3.0
85
+ version: