tf2r 0.3.1 → 0.4.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 +4 -4
- data/.coveralls.yml +1 -0
- data/Gemfile.lock +1 -1
- data/lib/tf2r/api.rb +19 -2
- data/lib/tf2r/raffle.rb +37 -0
- data/lib/tf2r/scraper.rb +2 -2
- data/lib/tf2r/text_helpers.rb +7 -1
- data/lib/tf2r/version.rb +1 -1
- data/spec/raffle_spec.rb +43 -10
- data/spec/scraper_spec.rb +17 -61
- data/spec/spec_helper.rb +4 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d040ee6e9265eed2195362f8c115193590c62207
|
4
|
+
data.tar.gz: be7f1f7cf1a805e4fa5b16fc02fd41b0692193c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51aef9d710a7e17129106d3ec3f0843f822cd7e43e5a89e39de7fa48f324877437028fc0f75a3e6a4b7c946d5085dd7a7e4eb721305c0ca5e06bb4fcb134a068
|
7
|
+
data.tar.gz: 00ea3e40215556e898e0a9e3c078bab6506083e4f11cac1ca4ee4eb97e36688e7c85ef988586b576fc065130f531a0e39e0a49a760dbba45b02c7b9113b66033
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/Gemfile.lock
CHANGED
data/lib/tf2r/api.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module TF2R
|
2
2
|
# This class handles interaction with TF2R's "API", which is really just an
|
3
3
|
# open endpoint located at http://tf2r.com/job.php, in use all over the site
|
4
|
-
#
|
4
|
+
# for dynamically updating pages.
|
5
5
|
class API
|
6
6
|
HOST = 'http://tf2r.com'
|
7
7
|
ENDPOINT = '/job.php'
|
@@ -9,6 +9,22 @@ module TF2R
|
|
9
9
|
# a problem for any raffles with a maximum entries greater than 2500.
|
10
10
|
MAX_ENTRY_RESPONSE_COUNT = 2500
|
11
11
|
|
12
|
+
# Queries the API for information about a raffle.
|
13
|
+
#
|
14
|
+
# @params link_snippet [String] the link snippet of the desired raffle.
|
15
|
+
# @return [Array] the API response for the raffle.
|
16
|
+
# * ended (+Boolean+) whether the raffle is done.
|
17
|
+
# * timeleft (+Fixnum+) seconds remaining.
|
18
|
+
# * max_entry (+Fixnum+) maximum number of entries.
|
19
|
+
# * entry (+String+) but really a number. This is the ID in TF2R's db of
|
20
|
+
# the last entry returned in the response.
|
21
|
+
# * wc (+Float+) the chance for any one participant to win.
|
22
|
+
# * cur_entry (+Fixnum+) current number of entries.
|
23
|
+
# * started (+Boolean+) whether the raffle has begun.
|
24
|
+
# * newentry (+Array+) contains hashes representing participants in
|
25
|
+
# chronological order.
|
26
|
+
# * chaten (+Array+) I have no idea.
|
27
|
+
# * chatmax (+Fixnum) ¯\_(ツ)_/¯
|
12
28
|
def self.raffle_info(link_snippet)
|
13
29
|
params = {checkraffle: true, rid: link_snippet[1..-1],
|
14
30
|
lastentrys: 0, lastchat: 0}
|
@@ -17,7 +33,8 @@ module TF2R
|
|
17
33
|
|
18
34
|
private
|
19
35
|
|
20
|
-
#
|
36
|
+
# Submits a request to job.php with the given params.
|
37
|
+
# TODO: should this do something with the responses headers?
|
21
38
|
def self.request(params)
|
22
39
|
uri = URI.parse(HOST)
|
23
40
|
http = Net::HTTP.new(uri.host, uri.port)
|
data/lib/tf2r/raffle.rb
CHANGED
@@ -15,6 +15,34 @@ module TF2R
|
|
15
15
|
get_full_participants if max_entries > API::MAX_ENTRY_RESPONSE_COUNT
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
# Gives information about the raffle.
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# r = Raffle.new('kstzcbd')
|
23
|
+
# r.info #=>
|
24
|
+
# {:link_snippet=>"kstzcbd",
|
25
|
+
# :title=>"Just one refined [1 hour]",
|
26
|
+
# :description=>"Plain and simple.",
|
27
|
+
# :start_time=>2012-10-29 09:51:45 -0400,
|
28
|
+
# :end_time=>2012-10-29 09:53:01 -0400,
|
29
|
+
# :win_chance=>0.1,
|
30
|
+
# :current_entries=>10,
|
31
|
+
# :max_entries=>10,
|
32
|
+
# :is_done=>true}
|
33
|
+
#
|
34
|
+
# @param page [Mechanize::Page] the raffle page.
|
35
|
+
# @return [Hash] a representation of the raffle.
|
36
|
+
# * :link_snippet (+String+) — the "raffle id" in the URL.
|
37
|
+
# * :title (+String+) — the raffle's title.
|
38
|
+
# * :description (+String+) — the raffle's "message".
|
39
|
+
# * :start_time (+Time+) — the creation time of the raffle.
|
40
|
+
# * :end_time (+Time+) — the projects/observed end time for the raffle.
|
41
|
+
# * :win_chance (+Float+) — a participant's chance to win the raffle.
|
42
|
+
# Rounded to 5 digits.
|
43
|
+
# * :current_entries (+Fixnum+) — the current number of participants.
|
44
|
+
# * :max_entries (+Fixnum+) — the maximum number of particpants allowed.
|
45
|
+
# * :is_done (+Boolean+) — whether new users can enter the raffle.
|
18
46
|
def info
|
19
47
|
@info ||= {link_snippet: @link_snippet, title: title,
|
20
48
|
description: description, start_time: start_time,
|
@@ -23,6 +51,11 @@ module TF2R
|
|
23
51
|
is_done: is_done}
|
24
52
|
end
|
25
53
|
|
54
|
+
# Gives information about the raffle's creator.
|
55
|
+
#
|
56
|
+
# Taken straight from +Scraper+.
|
57
|
+
#
|
58
|
+
# @see Scraper#scrape_raffle_for_creator
|
26
59
|
def creator
|
27
60
|
@scraper_info[1]
|
28
61
|
end
|
@@ -38,6 +71,8 @@ module TF2R
|
|
38
71
|
|
39
72
|
private
|
40
73
|
|
74
|
+
# Asynchronously makes network connections to TF2R to query its API and
|
75
|
+
# scrape the raffle page.
|
41
76
|
def populate_raffle_info
|
42
77
|
threads = []
|
43
78
|
threads << Thread.new do
|
@@ -56,6 +91,8 @@ module TF2R
|
|
56
91
|
@full_participants = @scraper.scrape_raffle_for_participants(page)
|
57
92
|
end
|
58
93
|
|
94
|
+
# Converts the representation of participants by TF2R's API into our own
|
95
|
+
# standard representation.
|
59
96
|
def normalize_entries(entries)
|
60
97
|
entries.map do |entry|
|
61
98
|
entry[:steam_id] = extract_steam_id entry.delete('link')
|
data/lib/tf2r/scraper.rb
CHANGED
@@ -4,6 +4,7 @@ module TF2R
|
|
4
4
|
include TF2R::TextHelpers
|
5
5
|
|
6
6
|
class InvalidUserPage < StandardError; end
|
7
|
+
|
7
8
|
# Creates a Scraper. Pass values using the options hash.
|
8
9
|
#
|
9
10
|
# :user_agent a String used for the User-Agent header
|
@@ -207,12 +208,11 @@ module TF2R
|
|
207
208
|
raise InvalidUserPage, 'The given page does not correspond to any user.'
|
208
209
|
else
|
209
210
|
infos = user_page.parser.css('.raffle_infomation') #sic
|
210
|
-
avatar_anchor = infos[0].css('img')[0]
|
211
211
|
user_anchor = infos[1].css('a')[0]
|
212
212
|
|
213
213
|
steam_id = extract_steam_id(user_page.uri.to_s)
|
214
214
|
username = /TF2R Item Raffles - (.+)/.match(user_page.title)[1]
|
215
|
-
avatar_link =
|
215
|
+
avatar_link = infos[0].css('img')[0].attribute('src').to_s
|
216
216
|
|
217
217
|
posrep = infos.css('.upvb').text.to_i
|
218
218
|
negrep = infos.css('.downvb').text.to_i
|
data/lib/tf2r/text_helpers.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module TF2R
|
2
|
-
#
|
2
|
+
# This module provides common methods for manipulating text throughout this
|
3
|
+
# gem. Common operations include messing with Steam ID 64s, hex color codes,
|
4
|
+
# and link to resources on TF2R.
|
3
5
|
module TextHelpers
|
4
6
|
# Extracts a hex colour code.
|
5
7
|
#
|
@@ -43,6 +45,10 @@ module TF2R
|
|
43
45
|
link = "http://tf2r.com/#{link_snippet}.html"
|
44
46
|
end
|
45
47
|
|
48
|
+
# @example
|
49
|
+
# raffle_link_full('kabc123') => 'http://tf2r.com/kabc123.html?full'
|
50
|
+
#
|
51
|
+
# @see raffle_link
|
46
52
|
def raffle_link_full(link_snippet)
|
47
53
|
"#{raffle_link(link_snippet)}?full"
|
48
54
|
end
|
data/lib/tf2r/version.rb
CHANGED
data/spec/raffle_spec.rb
CHANGED
@@ -8,23 +8,56 @@ describe TF2R::Raffle do
|
|
8
8
|
}
|
9
9
|
|
10
10
|
describe '#info' do
|
11
|
-
let(:
|
11
|
+
let(:info) { raffle.info }
|
12
12
|
|
13
13
|
it 'returns correct values' do
|
14
|
-
expect(
|
15
|
-
expect(
|
16
|
-
expect(
|
17
|
-
expect(
|
14
|
+
expect(info[:link_snippet]).to eql('kstzcbd')
|
15
|
+
expect(info[:title]).to eql('Just one refined [1 hour]')
|
16
|
+
expect(info[:description]).to eql('Plain and simple.')
|
17
|
+
expect(info[:start_time]).to eql(
|
18
18
|
Time.new(2012, 10, 29, 14, 51, 45, '+01:00')
|
19
19
|
)
|
20
|
-
expect(
|
20
|
+
expect(info[:end_time]).to eql(
|
21
21
|
Time.new(2012, 10, 29, 14, 53, 01, '+01:00')
|
22
22
|
)
|
23
|
-
expect(
|
23
|
+
expect(info[:win_chance]).to eql(0.1)
|
24
24
|
# TODO: use a raffle where current_entries != max_entries
|
25
|
-
expect(
|
26
|
-
expect(
|
27
|
-
expect(
|
25
|
+
expect(info[:current_entries]).to eql(10)
|
26
|
+
expect(info[:max_entries]).to eql(10)
|
27
|
+
expect(info[:is_done]).to eql(true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#creator' do
|
32
|
+
let(:creator) { raffle.creator }
|
33
|
+
|
34
|
+
it 'returns the raffle creator' do
|
35
|
+
expect(creator[:steam_id]).to match(76561198061719848)
|
36
|
+
expect(creator[:username]).to eql('Yulli')
|
37
|
+
expect(creator[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
38
|
+
expect(creator[:posrep]).to eql(11459)
|
39
|
+
expect(creator[:negrep]).to eql(0)
|
40
|
+
expect(creator[:colour]).to match(HEX_COLOUR_REGEXP)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#participants' do
|
45
|
+
let(:participants) { raffle.participants }
|
46
|
+
|
47
|
+
it 'returns the correct number of participants' do
|
48
|
+
expect(participants.length).to eql(10)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns the correct participants' do
|
52
|
+
expect(participants[0][:steam_id]).to match(76561198017461530)
|
53
|
+
expect(participants[0][:username]).to eql('TheJohn')
|
54
|
+
expect(participants[0][:colour]).to match(HEX_COLOUR_REGEXP)
|
55
|
+
expect(participants[0][:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
56
|
+
|
57
|
+
expect(participants[-1][:steam_id]).to match(76561198029274581)
|
58
|
+
expect(participants[-1][:username]).to eql('pony danza (OUT...')
|
59
|
+
expect(participants[-1][:colour]).to match(HEX_COLOUR_REGEXP)
|
60
|
+
expect(participants[-1][:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
28
61
|
end
|
29
62
|
end
|
30
63
|
end
|
data/spec/scraper_spec.rb
CHANGED
@@ -8,12 +8,6 @@ describe TF2R::Scraper do
|
|
8
8
|
end
|
9
9
|
}
|
10
10
|
|
11
|
-
it 'is instantiable' do
|
12
|
-
expect{
|
13
|
-
TF2R::Scraper.new()
|
14
|
-
}.not_to raise_error
|
15
|
-
end
|
16
|
-
|
17
11
|
describe '#new' do
|
18
12
|
context 'no options are given' do
|
19
13
|
it 'creates an agent with default user agent if none is specified' do
|
@@ -53,51 +47,26 @@ describe TF2R::Scraper do
|
|
53
47
|
:headers => {'content_type' => 'text/html'})
|
54
48
|
end
|
55
49
|
|
56
|
-
it 'makes a request to the specified URL' do
|
57
|
-
expect(
|
58
|
-
scraper.fetch('http://google.com')
|
59
|
-
).to have_requested(:get, 'google.com')
|
60
|
-
end
|
61
|
-
|
62
50
|
it 'returns a Mechanize::Page' do
|
63
51
|
expect(scraper.fetch('http://google.com')).to be_a(Mechanize::Page)
|
64
52
|
end
|
65
53
|
end
|
66
54
|
|
67
|
-
describe '#scrape_raffle' do
|
68
|
-
let(:result) { scraper.scrape_raffle(raffle_page) }
|
69
|
-
|
70
|
-
it 'returns an Array' do
|
71
|
-
expect(result).to be_an(Array)
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'returns the same as its child methods' do
|
75
|
-
expect(result[0]).to eql(scraper.scrape_raffle_for_raffle(raffle_page))
|
76
|
-
expect(result[1]).to eql(scraper.scrape_raffle_for_creator(raffle_page))
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
55
|
describe '#scrape_main_page' do
|
81
56
|
let(:result) {
|
82
|
-
|
83
|
-
scraper.scrape_main_page
|
84
|
-
end
|
57
|
+
scraper.scrape_main_page
|
85
58
|
}
|
86
59
|
|
87
60
|
before(:each) do
|
88
61
|
raffles_file = File.new(File.join(File.dirname(__FILE__), 'raffles.html'))
|
62
|
+
|
89
63
|
stub_request(:get, "http://tf2r.com/raffles.html").
|
90
64
|
with(:headers => {'Accept'=>'*/*', 'Host'=>'tf2r.com'}).
|
91
65
|
to_return(:status => 200, :body => raffles_file,
|
92
66
|
:headers => {'content_type' => 'text/html'})
|
93
67
|
end
|
94
68
|
|
95
|
-
|
96
|
-
expect(result).to be_an(Array)
|
97
|
-
expect(result[0]).to be_a(String)
|
98
|
-
expect(result[-1]).to be_a(String)
|
99
|
-
end
|
100
|
-
|
69
|
+
# TODO: figure out a way to periodicially use more recent pages
|
101
70
|
it 'returns raffle links in reverse chronological order' do
|
102
71
|
# See spec/raffles.html for the hard-coded values.
|
103
72
|
expect(result[0]).to eql('http://tf2r.com/kzkp8jo.html')
|
@@ -105,12 +74,17 @@ describe TF2R::Scraper do
|
|
105
74
|
end
|
106
75
|
end
|
107
76
|
|
108
|
-
describe '#
|
109
|
-
let(:result) { scraper.
|
77
|
+
describe '#scrape_raffle' do
|
78
|
+
let(:result) { scraper.scrape_raffle(raffle_page) }
|
110
79
|
|
111
|
-
it 'returns
|
112
|
-
expect(result).to
|
80
|
+
it 'returns the same as its child methods' do
|
81
|
+
expect(result[0]).to eql(scraper.scrape_raffle_for_raffle(raffle_page))
|
82
|
+
expect(result[1]).to eql(scraper.scrape_raffle_for_creator(raffle_page))
|
113
83
|
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#scrape_raffle_for_creator' do
|
87
|
+
let(:result) { scraper.scrape_raffle_for_creator(raffle_page) }
|
114
88
|
|
115
89
|
# This is a terrible spec right now. The raffle tested against is one of
|
116
90
|
# my own, which is constantly updated as I use the site. If I gain or lose
|
@@ -118,13 +92,13 @@ describe TF2R::Scraper do
|
|
118
92
|
# TODO: Use a stagnant alt account to make a test raffle.
|
119
93
|
it 'returns correct values' do
|
120
94
|
expect(result[:steam_id]).to eql(76561198061719848)
|
121
|
-
expect(result[:colour]).to
|
95
|
+
expect(result[:colour]).to match(HEX_COLOUR_REGEXP)
|
122
96
|
|
123
97
|
# The following expectations are very brittle. TF2R updates the certain
|
124
98
|
# user attributes (such as name and avatar) every time the user logs in
|
125
99
|
# with new ones.
|
126
100
|
expect(result[:username]).to eql('Yulli')
|
127
|
-
expect(result[:avatar_link]).to
|
101
|
+
expect(result[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
128
102
|
|
129
103
|
# Talk about brittle.
|
130
104
|
expect(result[:posrep]).to eql(11459)
|
@@ -136,10 +110,6 @@ describe TF2R::Scraper do
|
|
136
110
|
describe '#scrape_raffle_for_raffle' do
|
137
111
|
let(:result) { scraper.scrape_raffle_for_raffle(raffle_page) }
|
138
112
|
|
139
|
-
it 'returns a Hash representation of a raffle' do
|
140
|
-
expect(result).to be_a(Hash)
|
141
|
-
end
|
142
|
-
|
143
113
|
it 'returns correct values' do
|
144
114
|
expect(result[:link_snippet]).to eql('kstzcbd')
|
145
115
|
expect(result[:title]).to eql('Just one refined [1 hour]')
|
@@ -154,6 +124,7 @@ describe TF2R::Scraper do
|
|
154
124
|
end
|
155
125
|
|
156
126
|
describe '#scrape_raffle_for_participants' do
|
127
|
+
# TODO: use a real test raffle
|
157
128
|
let(:page) {
|
158
129
|
VCR.use_cassette('raffle_kstzcbd_full') do
|
159
130
|
scraper.fetch('http://tf2r.com/kstzcbd.html?full')
|
@@ -161,12 +132,6 @@ describe TF2R::Scraper do
|
|
161
132
|
}
|
162
133
|
let(:result) { scraper.scrape_raffle_for_participants(page) }
|
163
134
|
|
164
|
-
it 'returns an Array of Hashes' do
|
165
|
-
expect(result).to be_an(Array)
|
166
|
-
expect(result[0]).to be_a(Hash)
|
167
|
-
expect(result[-1]).to be_a(Hash)
|
168
|
-
end
|
169
|
-
|
170
135
|
it 'returns correct values' do
|
171
136
|
expect(result[0][:steam_id]).to eql(76561198017461530)
|
172
137
|
expect(result[0][:username]).to eql('TheJohn')
|
@@ -187,17 +152,13 @@ describe TF2R::Scraper do
|
|
187
152
|
}
|
188
153
|
let(:result) { scraper.scrape_user(user_page) }
|
189
154
|
|
190
|
-
it 'returns a Hash' do
|
191
|
-
expect(result).to be_a(Hash)
|
192
|
-
end
|
193
|
-
|
194
155
|
# This spec is also brittle. It relies on my TF2R user account, which is
|
195
156
|
# updated constantly.
|
196
157
|
# TODO: use a stagnant alt account
|
197
158
|
it 'returns correct values' do
|
198
159
|
expect(result[:steam_id]).to eql(76561198061719848)
|
199
160
|
expect(result[:username]).to eql('Yulli')
|
200
|
-
expect(result[:avatar_link]).to
|
161
|
+
expect(result[:avatar_link]).to match(STEAM_AVATAR_LINK_REGEXP)
|
201
162
|
expect(result[:posrep]).to eql(11459)
|
202
163
|
expect(result[:negrep]).to eql(0)
|
203
164
|
expect(result[:colour]).to eql('70b01b')
|
@@ -228,12 +189,6 @@ describe TF2R::Scraper do
|
|
228
189
|
}
|
229
190
|
let(:result) { scraper.scrape_ranks(info_page) }
|
230
191
|
|
231
|
-
it 'returns an Array of Hashes' do
|
232
|
-
expect(result).to be_an(Array)
|
233
|
-
expect(result[0]).to be_a(Hash)
|
234
|
-
expect(result[-1]).to be_a(Hash)
|
235
|
-
end
|
236
|
-
|
237
192
|
it 'returns correct values' do
|
238
193
|
expect(result[0][:name]).to eql('User')
|
239
194
|
expect(result[0][:description]).to eql(
|
@@ -242,6 +197,7 @@ describe TF2R::Scraper do
|
|
242
197
|
expect(result[0][:colour]).to eql('ebe2ca')
|
243
198
|
|
244
199
|
# This is brittle. Every time a new rank is added, this fails.
|
200
|
+
# TODO: explore possibilities to limber this up
|
245
201
|
expect(result[-1][:name]).to eql('Lense')
|
246
202
|
expect(result[-1][:description]).to eql('[ Custom donator rank ]')
|
247
203
|
expect(result[-1][:colour]).to eql('545d6c')
|
data/spec/spec_helper.rb
CHANGED
@@ -23,4 +23,7 @@ VCR.configure do |c|
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
WebMock.disable_net_connect!(allow_localhost: true)
|
26
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
27
|
+
|
28
|
+
STEAM_AVATAR_LINK_REGEXP = /^http:\/\/media\.steampowered\.com\/steamcommunity\/public\/images\/avatars\/\w{2}\/[\w]+\.jpg$/
|
29
|
+
HEX_COLOUR_REGEXP = /^[a-f0-9]{3}{1,2}$/i
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tf2r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Kim
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -143,6 +143,7 @@ executables: []
|
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
145
145
|
files:
|
146
|
+
- ".coveralls.yml"
|
146
147
|
- ".gitignore"
|
147
148
|
- ".ruby-gemset"
|
148
149
|
- ".ruby-version"
|