ruby_reddit_api 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README +47 -0
- data/features/api.feature +10 -0
- data/features/base.feature +15 -0
- data/features/comment.feature +40 -0
- data/features/message.feature +5 -0
- data/features/reddit.yml +5 -0
- data/features/step_definitions/api_steps.rb +22 -0
- data/features/step_definitions/base_steps.rb +49 -0
- data/features/step_definitions/comment_steps.rb +59 -0
- data/features/step_definitions/message_steps.rb +18 -0
- data/features/step_definitions/submission_steps.rb +117 -0
- data/features/step_definitions/user_steps.rb +25 -0
- data/features/submission.feature +58 -0
- data/features/support/base_helpers.rb +17 -0
- data/features/user.feature +11 -0
- data/lib/ruby_reddit_api.rb +17 -0
- data/lib/ruby_reddit_api/api.rb +46 -0
- data/lib/ruby_reddit_api/base.rb +109 -0
- data/lib/ruby_reddit_api/comment.rb +86 -0
- data/lib/ruby_reddit_api/json_listing.rb +38 -0
- data/lib/ruby_reddit_api/message.rb +30 -0
- data/lib/ruby_reddit_api/submission.rb +99 -0
- data/lib/ruby_reddit_api/user.rb +47 -0
- data/lib/ruby_reddit_api/version.rb +3 -0
- data/lib/ruby_reddit_api/vote.rb +33 -0
- metadata +129 -0
data/README
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Ruby Reddit Client v0.1.9
|
2
|
+
==================
|
3
|
+
Tested with 1.9.2
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
=======
|
7
|
+
> require "ruby_reddit_api"
|
8
|
+
> r = Reddit::Api.new "user", "password"
|
9
|
+
> results = r.browse "ruby"
|
10
|
+
> results[0].upvote
|
11
|
+
|
12
|
+
Features:
|
13
|
+
========
|
14
|
+
- Authentication
|
15
|
+
- Parse submissions
|
16
|
+
- Comments parsing
|
17
|
+
- Vote on submission
|
18
|
+
|
19
|
+
|
20
|
+
Running Tests:
|
21
|
+
=============
|
22
|
+
- Set up the reddit VM (http://blog.reddit.com/2010/05/admins-never-do-what-you-want-now-it-is.html)
|
23
|
+
- Ensure your hosts file has reddit.local pointing to the VM
|
24
|
+
- rake cucumber
|
25
|
+
|
26
|
+
License
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2010 James Cook
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
32
|
+
of this software and associated documentation files (the "Software"), to deal
|
33
|
+
in the Software without restriction, including without limitation the rights
|
34
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
35
|
+
copies of the Software, and to permit persons to whom the Software is
|
36
|
+
furnished to do so, subject to the following conditions:
|
37
|
+
|
38
|
+
The above copyright notice and this permission notice shall be included in
|
39
|
+
all copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
42
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
43
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
44
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
45
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
46
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
47
|
+
THE SOFTWARE.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Browsing
|
2
|
+
I want to be able to browse submissions
|
3
|
+
|
4
|
+
Scenario: Valid subreddit
|
5
|
+
Given I submit a valid subreddit
|
6
|
+
Then I should get back a listing of submissions
|
7
|
+
|
8
|
+
Scenario: Invalid subreddit
|
9
|
+
Given I submit a invalid subreddit
|
10
|
+
Then I should get back some error
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: Authentication
|
2
|
+
In order perform things like voting and commenting
|
3
|
+
As a Redditor
|
4
|
+
I want to be able to login in my step definitions
|
5
|
+
|
6
|
+
Scenario: Valid user and password
|
7
|
+
Given I have a valid user
|
8
|
+
And I send my user and password to the login API
|
9
|
+
Then I should have a valid cookie
|
10
|
+
And the stored headers should refer to the cookie
|
11
|
+
|
12
|
+
Scenario: Invalid user and password
|
13
|
+
Given I have a invalid user
|
14
|
+
And I send my user and password to the login API
|
15
|
+
Then I should not have a valid cookie
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Comments
|
2
|
+
I want to be able to comment on a comment
|
3
|
+
I want to be able to delete my comments
|
4
|
+
|
5
|
+
Scenario: Valid comment text
|
6
|
+
Given I enter some text
|
7
|
+
Then I should be able to add a comment
|
8
|
+
|
9
|
+
Scenario: I want to hide a comment
|
10
|
+
Given I have a comment
|
11
|
+
Then I should be able to hide the comment
|
12
|
+
|
13
|
+
Scenario: I want to remove a comment
|
14
|
+
Given I have a comment
|
15
|
+
Then I should be able to remove the comment
|
16
|
+
|
17
|
+
Scenario: I want to approve a comment
|
18
|
+
Given I have a comment
|
19
|
+
Then I should be able to approve the comment
|
20
|
+
|
21
|
+
Scenario: I want to edit a comment
|
22
|
+
Given I have a comment
|
23
|
+
Then I should be able to edit the comment
|
24
|
+
|
25
|
+
Scenario: I want to comment on a comment
|
26
|
+
Given I have a comment
|
27
|
+
Then I should be able to reply to the comment
|
28
|
+
|
29
|
+
Scenario: I want to moderator distinguish a comment
|
30
|
+
Given I have a comment
|
31
|
+
Then I should be able to moderator distinguish the comment
|
32
|
+
|
33
|
+
Scenario: I want to indistinguish a comment
|
34
|
+
Given I have a comment
|
35
|
+
Then I should be able to indistinguish the comment
|
36
|
+
|
37
|
+
Scenario: I want to admin distinguish a comment
|
38
|
+
Given I have a comment
|
39
|
+
Then I should be able to admin distinguish the comment
|
40
|
+
|
data/features/reddit.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Given /^I submit a valid subreddit$/ do
|
2
|
+
Reddit::Api.base_uri "reddit.local"
|
3
|
+
@api = Reddit::Api.new "reddit", "password"
|
4
|
+
@subreddit = "reddit_test0"
|
5
|
+
end
|
6
|
+
|
7
|
+
Then /^I should get back a listing of submissions$/ do
|
8
|
+
results = @api.browse(@subreddit)
|
9
|
+
results[0].class.should == Reddit::Submission
|
10
|
+
results[0].author.should_not == nil
|
11
|
+
end
|
12
|
+
|
13
|
+
Given /^I submit a invalid subreddit$/ do
|
14
|
+
Reddit::Api.base_uri "reddit.local"
|
15
|
+
@api = Reddit::Api.new "reddit", "password"
|
16
|
+
@subreddit = "invalid_subreddit"
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /^I should get back some error$/ do
|
20
|
+
results = @api.browse(@subreddit)
|
21
|
+
results.should == false
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join( File.dirname(__FILE__), "..", "..", "lib", "ruby_reddit_api.rb")
|
2
|
+
Before do
|
3
|
+
Reddit::Base.instance_variable_set("@cookie", nil)
|
4
|
+
load_server_config
|
5
|
+
Reddit::Api.base_uri @address
|
6
|
+
end
|
7
|
+
|
8
|
+
Given /^I have a valid user$/ do
|
9
|
+
load_server_config
|
10
|
+
end
|
11
|
+
|
12
|
+
Given /^I have a invalid user$/ do
|
13
|
+
@user, @pass = "invalid", "user"
|
14
|
+
end
|
15
|
+
|
16
|
+
Given /^I send my user and password to the login API$/ do
|
17
|
+
@api = Reddit::Api.new @user, @pass
|
18
|
+
@api.login
|
19
|
+
end
|
20
|
+
|
21
|
+
Given /I'm logged in$/ do
|
22
|
+
@api = Reddit::Api.new @user, @pass
|
23
|
+
@api.login
|
24
|
+
end
|
25
|
+
|
26
|
+
Given /I'm not logged in$/ do
|
27
|
+
@api.logout if @api
|
28
|
+
@api = Reddit::Api.new @user, "bad pass"
|
29
|
+
@api.logout
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^I should have a valid cookie$/ do
|
33
|
+
@api.cookie.should_not == nil
|
34
|
+
@api.cookie.should =~ /reddit_session/
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /^I should not have a valid cookie$/ do
|
38
|
+
@api.cookie.should_not == nil
|
39
|
+
@api.cookie.should =~ /reddit_first/
|
40
|
+
end
|
41
|
+
|
42
|
+
Then /^I should not have a cookie$/ do
|
43
|
+
@api.cookie.should == nil
|
44
|
+
end
|
45
|
+
|
46
|
+
Then /^the stored headers should refer to the cookie$/ do
|
47
|
+
@api.base_headers["Cookie"].should == @api.cookie
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Before do
|
2
|
+
load_server_config
|
3
|
+
Reddit::Api.base_uri @address
|
4
|
+
Reddit::Submission.base_uri @address
|
5
|
+
Reddit::Comment.base_uri @address
|
6
|
+
@api = Reddit::Api.new @user, @pass
|
7
|
+
@api.login
|
8
|
+
@submission ||= @api.browse("reddit_test0")[0]
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /^I enter some text$/ do
|
12
|
+
@text = "SOME COMMENT"
|
13
|
+
end
|
14
|
+
|
15
|
+
Then /^I should be able to add a comment$/ do
|
16
|
+
@submission.add_comment(@text).should be true
|
17
|
+
end
|
18
|
+
|
19
|
+
Given /^I have a comment$/ do
|
20
|
+
@submission.add_comment("a comment")
|
21
|
+
@comment = @submission.comments.last
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^I should be able to hide the comment$/ do
|
25
|
+
@comment.hide
|
26
|
+
end
|
27
|
+
|
28
|
+
Then /^I should be able to remove the comment$/ do
|
29
|
+
@comment.remove
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^I should be able to approve the comment$/ do
|
33
|
+
@comment.approve
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^I should not be able to edit the comment$/ do
|
37
|
+
@comment.edit( "1234" ).should be false
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^I should be able to edit the comment$/ do
|
41
|
+
@comment.edit( "1234" ).should be true
|
42
|
+
end
|
43
|
+
|
44
|
+
Then /^I should be able to moderator distinguish the comment$/ do
|
45
|
+
pending #@comment.moderator_distinguish
|
46
|
+
end
|
47
|
+
|
48
|
+
Then /^I should be able to indistinguish the comment$/ do
|
49
|
+
pending #@comment.indistinguish
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /^I should be able to admin distinguish the comment$/ do
|
53
|
+
pending #@comment.admin_distinguish
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^I should be able to reply to the comment$/ do
|
57
|
+
pending #@comment.reply("a reply").should be true
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Before do
|
2
|
+
load_server_config
|
3
|
+
Reddit::Api.base_uri @address
|
4
|
+
Reddit::Message.base_uri @address
|
5
|
+
end
|
6
|
+
|
7
|
+
Given /^I am logged in$/ do
|
8
|
+
@api = Reddit::Api.new @user, @pass
|
9
|
+
@api.login
|
10
|
+
end
|
11
|
+
|
12
|
+
When /^I go to the sent messages page$/ do
|
13
|
+
@result = @api.sent_messages
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^I should see a listing of messages$/ do
|
17
|
+
@result.any?{|r| r.is_a?(Reddit::Message) == false}.should be false
|
18
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
Given /^I have a submission$/ do
|
2
|
+
load_server_config
|
3
|
+
Reddit::Api.base_uri @address
|
4
|
+
Reddit::Submission.base_uri @address
|
5
|
+
Reddit::Comment.base_uri @address
|
6
|
+
@api = Reddit::Api.new @user, @pass
|
7
|
+
@api.login
|
8
|
+
@submission = @api.browse("reddit_test0")[0]
|
9
|
+
if @api.logged_in?
|
10
|
+
@submission.add_comment("STOP REPOSTING!!1") if @submission
|
11
|
+
else
|
12
|
+
raise "Can't run test. Submit failed.."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^I should be able to see the author$/ do
|
17
|
+
@submission.author.should_not == nil
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^I should be able to see the title$/ do
|
21
|
+
@submission.title.should_not == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^I should be able to see the selftext$/ do
|
25
|
+
@submission.selftext.should_not == nil
|
26
|
+
end
|
27
|
+
|
28
|
+
Then /^I should be able to see the url$/ do
|
29
|
+
@submission.url.should_not == nil
|
30
|
+
end
|
31
|
+
|
32
|
+
Then /^I should be able to see the up votes$/ do
|
33
|
+
@submission.ups.should_not == nil
|
34
|
+
end
|
35
|
+
|
36
|
+
Then /^I should be able to see the down votes$/ do
|
37
|
+
@submission.downs.should_not == nil
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^I should not be able to upvote it$/ do
|
41
|
+
@submission.upvote.should be false
|
42
|
+
end
|
43
|
+
|
44
|
+
Then /^I should not be able to downvote it$/ do
|
45
|
+
@submission.downvote.should be false
|
46
|
+
end
|
47
|
+
|
48
|
+
Then /^I should be able to upvote it$/ do
|
49
|
+
@submission.upvote.should be true
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /^I should be able to downvote it$/ do
|
53
|
+
@submission.downvote.should be true
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^I should be able to see the comments$/ do
|
57
|
+
comments = @submission.comments
|
58
|
+
comments.size.should > 0
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^I should not be able to save the submission$/ do
|
62
|
+
@submission.save.should be false
|
63
|
+
end
|
64
|
+
|
65
|
+
Then /^I should not be able to unsave the submission$/ do
|
66
|
+
@submission.unsave.should be false
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^I should not be able to hide the submission$/ do
|
70
|
+
@submission.hide.should be false
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^I should not be able to unhide the submission$/ do
|
74
|
+
@submission.unhide.should be false
|
75
|
+
end
|
76
|
+
|
77
|
+
Then /^I should be able to save the submission$/ do
|
78
|
+
@submission.save.should be true
|
79
|
+
end
|
80
|
+
|
81
|
+
Then /^I should be able to unsave the submission$/ do
|
82
|
+
@submission.unsave.should be true
|
83
|
+
end
|
84
|
+
|
85
|
+
Then /^I should be able to hide the submission$/ do
|
86
|
+
result = @submission.hide
|
87
|
+
@submission.unhide if result
|
88
|
+
result.should be true
|
89
|
+
end
|
90
|
+
|
91
|
+
Then /^I should be able to unhide the submission$/ do
|
92
|
+
@submission.unhide.should be true
|
93
|
+
end
|
94
|
+
|
95
|
+
Then /^I should be able to report the submission$/ do
|
96
|
+
@submission.report.should be true
|
97
|
+
end
|
98
|
+
|
99
|
+
Then /^I should not be able to report the submission$/ do
|
100
|
+
@submission.report.should be false
|
101
|
+
end
|
102
|
+
|
103
|
+
Then /^I should be able to see more comments if needed$/ do
|
104
|
+
pending
|
105
|
+
end
|
106
|
+
|
107
|
+
Then /^I should be able to moderator distinguish the submission$/ do
|
108
|
+
@submission.moderator_distinguish
|
109
|
+
end
|
110
|
+
|
111
|
+
Then /^I should be able to admin distinguish the submission$/ do
|
112
|
+
@submission.admin_distinguish
|
113
|
+
end
|
114
|
+
|
115
|
+
Then /^I should be able to indistinguish the submission$/ do
|
116
|
+
@submission.indistinguish
|
117
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Before do
|
2
|
+
load_server_config
|
3
|
+
Reddit::Api.base_uri @address
|
4
|
+
Reddit::Submission.base_uri @address
|
5
|
+
Reddit::Comment.base_uri @address
|
6
|
+
Reddit::User.base_uri @address
|
7
|
+
@api = Reddit::Api.new @user, @pass
|
8
|
+
@api.login
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /^I select a redditor$/ do
|
12
|
+
@submission = @api.browse("reddit_test1")[0]
|
13
|
+
@user = @submission.author
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^I should be able to friend them$/ do
|
17
|
+
@user.friend.should be true
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^I should be able to unfriend them$/ do
|
21
|
+
@user.unfriend.should be true
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
Feature: Submissions
|
2
|
+
I want to be able to peruse the submission details
|
3
|
+
And I want to be able to up vote the submission
|
4
|
+
And I want to be able to down vote the submission
|
5
|
+
And I want to be able to save submissions I like
|
6
|
+
And I want to be able to unsave submissions
|
7
|
+
|
8
|
+
|
9
|
+
Scenario: Viewing a submission
|
10
|
+
Scenario: When not logged in
|
11
|
+
Given I have a submission
|
12
|
+
And I'm not logged in
|
13
|
+
Then I should be able to see the author
|
14
|
+
And I should be able to see the title
|
15
|
+
And I should be able to see the selftext
|
16
|
+
And I should be able to see the url
|
17
|
+
And I should be able to see the up votes
|
18
|
+
And I should be able to see the down votes
|
19
|
+
And I should be able to see the comments
|
20
|
+
But I should not be able to upvote it
|
21
|
+
But I should not be able to downvote it
|
22
|
+
But I should not be able to save the submission
|
23
|
+
But I should not be able to unsave the submission
|
24
|
+
But I should not be able to hide the submission
|
25
|
+
But I should not be able to unhide the submission
|
26
|
+
But I should not be able to report the submission
|
27
|
+
And I should be able to see more comments if needed
|
28
|
+
|
29
|
+
Scenario: When logged in
|
30
|
+
Given I have a submission
|
31
|
+
And I'm logged in
|
32
|
+
Then I should be able to see the author
|
33
|
+
And I should be able to see the title
|
34
|
+
And I should be able to see the selftext
|
35
|
+
And I should be able to see the url
|
36
|
+
And I should be able to see the up votes
|
37
|
+
And I should be able to see the down votes
|
38
|
+
And I should be able to see the comments
|
39
|
+
And I should be able to upvote it
|
40
|
+
And I should be able to downvote it
|
41
|
+
And I should be able to save the submission
|
42
|
+
And I should be able to unsave the submission
|
43
|
+
And I should be able to hide the submission
|
44
|
+
And I should be able to unhide the submission
|
45
|
+
And I should be able to report the submission
|
46
|
+
And I should be able to see more comments if needed
|
47
|
+
|
48
|
+
Scenario: I want to moderator distinguish a submission
|
49
|
+
Given I have a submission
|
50
|
+
Then I should be able to moderator distinguish the submission
|
51
|
+
|
52
|
+
Scenario: I want to admin distinguish a submission
|
53
|
+
Given I have a submission
|
54
|
+
Then I should be able to admin distinguish the submission
|
55
|
+
|
56
|
+
Scenario: I want to indistinguish a submission
|
57
|
+
Given I have a submission
|
58
|
+
Then I should be able to indistinguish the submission
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module BaseHelpers
|
2
|
+
def load_server_config
|
3
|
+
@address, @post, @user, @pass = parse_config.compact
|
4
|
+
end
|
5
|
+
|
6
|
+
protected
|
7
|
+
def parse_config
|
8
|
+
file = File.exist?("features/reddit.yml") ? YAML.load_file("features/reddit.yml") : {}
|
9
|
+
server = file.fetch("server"){ server_defaults }
|
10
|
+
return [ server["address"], server["port"], server["test_user"], server["test_pass"] ]
|
11
|
+
end
|
12
|
+
|
13
|
+
def server_defaults
|
14
|
+
{"address" => "reddit.local", "port" => "80", "test_user" => "reddit", "test_pass" => "password"}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
World(BaseHelpers)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
module Reddit
|
4
|
+
end
|
5
|
+
|
6
|
+
require "httparty"
|
7
|
+
require "json"
|
8
|
+
require "reddit/version"
|
9
|
+
require "reddit/base"
|
10
|
+
require "reddit/json_listing"
|
11
|
+
require "reddit/api"
|
12
|
+
require "reddit/user"
|
13
|
+
require "reddit/vote"
|
14
|
+
require "reddit/submission"
|
15
|
+
require "reddit/comment"
|
16
|
+
require "reddit/message"
|
17
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Reddit
|
2
|
+
|
3
|
+
class Api < Base
|
4
|
+
attr_reader :last_action, :debug
|
5
|
+
|
6
|
+
def initialize(user=nil,password=nil, options={})
|
7
|
+
@user = user
|
8
|
+
@password = password
|
9
|
+
@debug = StringIO.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"<Reddit::Api user='#{user}'>"
|
14
|
+
end
|
15
|
+
|
16
|
+
def browse(subreddit, options={})
|
17
|
+
subreddit = sanitize_subreddit(subreddit)
|
18
|
+
options.merge! :handler => "Submission"
|
19
|
+
if options[:limit]
|
20
|
+
options.merge!({:query => {:limit => options[:limit]}})
|
21
|
+
end
|
22
|
+
read("/r/#{subreddit}.json", options )
|
23
|
+
end
|
24
|
+
|
25
|
+
def sent_messages
|
26
|
+
messages :sent
|
27
|
+
end
|
28
|
+
|
29
|
+
def received_messages
|
30
|
+
messages :inbox
|
31
|
+
end
|
32
|
+
|
33
|
+
def comments
|
34
|
+
messages :comments
|
35
|
+
end
|
36
|
+
|
37
|
+
def post_replies
|
38
|
+
messages :selfreply
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def messages(kind)
|
43
|
+
read("/message/#{kind.to_s}.json", :handler => "Message")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Reddit
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
|
5
|
+
attr_reader :last_action, :debug
|
6
|
+
base_uri "www.reddit.com"
|
7
|
+
class << self; attr_reader :cookie, :modhash, :user_id, :user, end
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
@debug = StringIO.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"<Reddit::Base user='#{user}'>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def login
|
18
|
+
capture_session(self.class.post( "/api/login", {:body => {:user => @user, :passwd => @password}, :debug_output => @debug} ) )
|
19
|
+
logged_in?
|
20
|
+
end
|
21
|
+
|
22
|
+
def logout
|
23
|
+
Reddit::Base.instance_variable_set("@cookie",nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
def cookie
|
27
|
+
Reddit::Base.cookie
|
28
|
+
end
|
29
|
+
|
30
|
+
def modhash
|
31
|
+
Reddit::Base.modhash
|
32
|
+
end
|
33
|
+
|
34
|
+
def user_id
|
35
|
+
Reddit::Base.user_id
|
36
|
+
end
|
37
|
+
|
38
|
+
def user
|
39
|
+
Reddit::Base.user
|
40
|
+
end
|
41
|
+
|
42
|
+
def logged_in?
|
43
|
+
!!(cookie && (cookie =~ /reddit_session/) != nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
def user_agent
|
47
|
+
self.class.user_agent
|
48
|
+
end
|
49
|
+
|
50
|
+
def base_headers
|
51
|
+
self.class.base_headers
|
52
|
+
end
|
53
|
+
|
54
|
+
def read(url, options={})
|
55
|
+
unless throttled?
|
56
|
+
@debug.rewind
|
57
|
+
verb = (options[:verb] || "get")
|
58
|
+
param_key = (verb == "get") ? :query : :body
|
59
|
+
resp = self.class.send( verb, url, {param_key => (options[param_key] || {}), :headers => base_headers, :debug_output => @debug})
|
60
|
+
if valid_response?(resp)
|
61
|
+
@last_action = Time.now
|
62
|
+
klass = Reddit.const_get(options[:handler] || "Submission")
|
63
|
+
resp = klass.parse( JSON.parse(resp.body, :max_nesting => 9_999) )
|
64
|
+
return resp
|
65
|
+
else
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
def valid_response?(response)
|
73
|
+
response.code == 200 && response.headers["content-type"].to_s =~ /json/
|
74
|
+
end
|
75
|
+
|
76
|
+
def capture_session(response)
|
77
|
+
cookies = response.headers["set-cookie"]
|
78
|
+
Reddit::Base.instance_variable_set("@cookie", cookies)
|
79
|
+
Reddit::Base.instance_variable_set("@user", @user)
|
80
|
+
end
|
81
|
+
|
82
|
+
def capture_user_id
|
83
|
+
return true if user_id
|
84
|
+
this_user = read("/user/#{user}/about.json", :handler => "User")
|
85
|
+
Reddit::Base.instance_variable_set("@user_id", this_user.id)
|
86
|
+
end
|
87
|
+
|
88
|
+
def throttled?
|
89
|
+
@last_action && ( ( Time.now - @last_action ) < 1.0 )
|
90
|
+
end
|
91
|
+
|
92
|
+
def sanitize_subreddit(subreddit)
|
93
|
+
subreddit.gsub!(/^\/?r\//,'')
|
94
|
+
subreddit.gsub!(/\.json\Z/,'')
|
95
|
+
subreddit
|
96
|
+
end
|
97
|
+
|
98
|
+
class << self
|
99
|
+
|
100
|
+
def base_headers
|
101
|
+
{'Cookie' => Reddit::Base.cookie.to_s, 'user-agent' => user_agent}
|
102
|
+
end
|
103
|
+
|
104
|
+
def user_agent
|
105
|
+
"Ruby Reddit Client v#{Reddit::VERSION}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Reddit
|
2
|
+
class Comment < Api
|
3
|
+
include JsonListing
|
4
|
+
attr_reader :body, :subreddit_id, :name, :created, :downs, :author, :created_utc, :body_html, :link_id, :parent_id, :likes, :replies, :subreddit, :ups, :debug, :kind
|
5
|
+
def initialize(json)
|
6
|
+
parse(json)
|
7
|
+
@debug = StringIO.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"<Reddit::Comment author='#{@author}' body='#{short_body}'>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
"#{kind}_#{@id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def author
|
19
|
+
@author_data ||= read("/user/#{@author}/about.json", :handler => "User")
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
body
|
24
|
+
end
|
25
|
+
|
26
|
+
def edit(newtext)
|
27
|
+
resp=self.class.post("/api/editusertext", {:body => {:thing_id => id, :uh => modhash, :r => subreddit, :text => newtext }, :headers => base_headers, :debug_output => @debug })
|
28
|
+
resp.code == 200
|
29
|
+
end
|
30
|
+
|
31
|
+
def hide # soft delete?
|
32
|
+
resp=self.class.post("/api/del", {:body => {:id => id, :uh => modhash, :r => subreddit, :executed => "removed" }, :headers => base_headers, :debug_output => @debug })
|
33
|
+
resp.code == 200
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove
|
37
|
+
resp=self.class.post("/api/remove", {:body => {:id => id, :uh => modhash, :r => subreddit}, :headers => base_headers, :debug_output => @debug })
|
38
|
+
resp.code == 200
|
39
|
+
end
|
40
|
+
|
41
|
+
def approve
|
42
|
+
resp=self.class.post("/api/approve", {:body => {:id => id, :uh => modhash, :r => subreddit}, :headers => base_headers, :debug_output => @debug })
|
43
|
+
resp.code == 200
|
44
|
+
end
|
45
|
+
|
46
|
+
def moderator_distinguish
|
47
|
+
add_distinction "yes"
|
48
|
+
end
|
49
|
+
|
50
|
+
def admin_distinguish
|
51
|
+
add_distinction "admin"
|
52
|
+
end
|
53
|
+
|
54
|
+
def indistinguish
|
55
|
+
add_distinction "no"
|
56
|
+
end
|
57
|
+
|
58
|
+
def reply(text)
|
59
|
+
resp = self.class.post("/api/comment", {:body => {:thing_id => id, :text => text, :uh => modhash, :r => subreddit }, :headers => base_headers, :debug_output => @debug })
|
60
|
+
resp.code == 200
|
61
|
+
end
|
62
|
+
|
63
|
+
def upvote
|
64
|
+
Vote.new(self).up
|
65
|
+
end
|
66
|
+
|
67
|
+
def downvote
|
68
|
+
Vote.new(self).down
|
69
|
+
end
|
70
|
+
|
71
|
+
def short_body
|
72
|
+
str = body.to_s
|
73
|
+
if str.size > 15
|
74
|
+
str[0..15] + "..."
|
75
|
+
else
|
76
|
+
body
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_distinction(verb)
|
81
|
+
resp=self.class.post("/api/distinguish/#{verb}", {:body => {:id => id, :uh => modhash, :r => subreddit, :executed => "distinguishing..."}, :headers => base_headers, :debug_output => @debug })
|
82
|
+
puts resp.headers
|
83
|
+
resp.code == 200
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Reddit
|
2
|
+
module JsonListing
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.send(:include, InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def parse(json)
|
10
|
+
results = []
|
11
|
+
if json.is_a?(Array)
|
12
|
+
json.each do |j|
|
13
|
+
results << parse(j)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
data = json["data"]
|
17
|
+
Reddit::Base.instance_variable_set("@modhash", data["modhash"]) # Needed for api calls
|
18
|
+
|
19
|
+
children = data["children"]
|
20
|
+
children.each do |message|
|
21
|
+
kind = message["kind"]
|
22
|
+
message["data"]["kind"] = kind
|
23
|
+
results << self.new(message["data"])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
return results.flatten
|
28
|
+
end
|
29
|
+
end
|
30
|
+
module InstanceMethods
|
31
|
+
def parse(json)
|
32
|
+
json.keys.each do |key|
|
33
|
+
instance_variable_set("@#{key}", json[key])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Reddit
|
2
|
+
class Message < Base
|
3
|
+
include JsonListing
|
4
|
+
attr_reader :body, :was_comment, :kind, :first_message, :name, :created, :dest, :created_utc, :body_html, :subreddit, :parent_id, :context, :replies, :subject, :debug
|
5
|
+
def initialize(json)
|
6
|
+
parse(json)
|
7
|
+
@debug = StringIO.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def id
|
11
|
+
"#{kind}_#{@id}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def author
|
15
|
+
@author_data ||= read("/user/#{@author}/about.json", :handler => "User")
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"<Reddit::Message '#{short_body}'>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def short_body
|
23
|
+
if body.size > 15
|
24
|
+
body[0..15]
|
25
|
+
else
|
26
|
+
body
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Reddit
|
2
|
+
class Submission < Base
|
3
|
+
include JsonListing
|
4
|
+
attr_reader :domain, :media_embed, :subreddit, :selftext_html, :selftext, :likes, :saved, :clicked, :media, :score, :over_18, :hidden, :thumbnail, :subreddit_id, :downs, :is_self, :permalink, :name, :created, :url, :title, :created_utc, :num_comments, :ups, :kind, :last_comment_id
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
parse(data)
|
8
|
+
@debug = StringIO.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"<Reddit::Submission id='#{id}' author='#{@author}' title='#{title}'>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def id
|
16
|
+
"#{kind}_#{@id}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def author
|
20
|
+
@author_data ||= read("/user/#{@author}/about.json", :handler => "User")
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_comment(text)
|
24
|
+
resp = self.class.post("/api/comment", {:body => {:thing_id => id, :text => text, :uh => modhash, :r => subreddit }, :headers => base_headers, :debug_output => @debug })
|
25
|
+
resp.code == 200
|
26
|
+
end
|
27
|
+
|
28
|
+
def upvote
|
29
|
+
Reddit::Vote.new(self).up
|
30
|
+
end
|
31
|
+
|
32
|
+
def downvote
|
33
|
+
Reddit::Vote.new(self).down
|
34
|
+
end
|
35
|
+
|
36
|
+
def save
|
37
|
+
toggle :save
|
38
|
+
end
|
39
|
+
|
40
|
+
def unsave
|
41
|
+
toggle :unsave
|
42
|
+
end
|
43
|
+
|
44
|
+
def hide
|
45
|
+
toggle :hide
|
46
|
+
end
|
47
|
+
|
48
|
+
def unhide
|
49
|
+
toggle :unhide
|
50
|
+
end
|
51
|
+
|
52
|
+
def report
|
53
|
+
toggle :report
|
54
|
+
end
|
55
|
+
|
56
|
+
def moderator_distinguish
|
57
|
+
add_distinction "yes"
|
58
|
+
end
|
59
|
+
|
60
|
+
def admin_distinguish
|
61
|
+
add_distinction "admin"
|
62
|
+
end
|
63
|
+
|
64
|
+
def indistinguish
|
65
|
+
add_distinction "no"
|
66
|
+
end
|
67
|
+
|
68
|
+
def comments(more=false)
|
69
|
+
#TODO Get morechildren to work correctly
|
70
|
+
if more && last_comment_id
|
71
|
+
opts = {:handler => "Comment",
|
72
|
+
:verb => "post",
|
73
|
+
:body =>
|
74
|
+
{:link_id => last_comment_id, :depth => 0, :r => subreddit, :uh => modhash, :renderstyle => "json", :pv_hex => "", :id => id}
|
75
|
+
}
|
76
|
+
return read("/api/morechildren", opts )
|
77
|
+
|
78
|
+
else
|
79
|
+
_comments = read( permalink + ".json", {:handler => "Comment", :query => {:limit => 50}} )
|
80
|
+
@last_comment_id = _comments.last.id if _comments && _comments.any?
|
81
|
+
return _comments
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
def add_distinction(verb)
|
87
|
+
resp=self.class.post("/api/distinguish/#{verb}", {:body => {:id => id, :uh => modhash, :r => subreddit, :executed => "distinguishing..."}, :headers => base_headers, :debug_output => @debug })
|
88
|
+
resp.code == 200
|
89
|
+
end
|
90
|
+
|
91
|
+
def toggle(which)
|
92
|
+
return false unless logged_in?
|
93
|
+
mapping = {:save => "save", :unsave => "unsave", :hide => "hidden", :unhide => "unhidden", :report => "report"}
|
94
|
+
mode = mapping[which]
|
95
|
+
resp = self.class.post("/api/#{which}", {:body => {:id => id, :uh => modhash, :r => subreddit, :executed => mode }, :headers => base_headers, :debug_output => @debug })
|
96
|
+
resp.code == 200
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
module Reddit
|
3
|
+
class User < Api
|
4
|
+
attr_reader :name, :debug, :created, :created_utc, :link_karma, :comment_karma, :is_mod, :has_mod_mail, :kind
|
5
|
+
def initialize(json)
|
6
|
+
@debug = StringIO.new
|
7
|
+
parse(json)
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"<Reddit::User name='#{name}'>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def id
|
15
|
+
"#{kind}_#{@id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
name
|
20
|
+
end
|
21
|
+
|
22
|
+
def friend
|
23
|
+
capture_user_id
|
24
|
+
resp=self.class.post("/api/friend", {:body => {:name => name, :container => user_id, :type => "friend", :uh => modhash}, :headers => base_headers, :debug_output => @debug })
|
25
|
+
resp.code == 200
|
26
|
+
end
|
27
|
+
|
28
|
+
def unfriend
|
29
|
+
capture_user_id
|
30
|
+
resp=self.class.post("/api/unfriend", {:body => {:name => name, :container => user_id, :type => "friend", :uh => modhash}, :headers => base_headers, :debug_output => @debug })
|
31
|
+
resp.code == 200
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def parse(json)
|
36
|
+
json.keys.each do |key|
|
37
|
+
instance_variable_set("@#{key}", json[key])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse(json)
|
42
|
+
kind, data = json["kind"], json["data"]
|
43
|
+
data["kind"] = kind
|
44
|
+
return self.new(data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Reddit
|
2
|
+
class Vote < Base
|
3
|
+
attr_reader :submission
|
4
|
+
|
5
|
+
def initialize(submission)
|
6
|
+
@submission = submission
|
7
|
+
end
|
8
|
+
|
9
|
+
def up
|
10
|
+
vote(:up)
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
vote(:down)
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
"<Reddit::Vote>"
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def vote(direction)
|
23
|
+
return false unless logged_in?
|
24
|
+
up_or_down = direction == :up ? 1 : -1
|
25
|
+
resp = self.class.post( "/api/vote", {:body => {:id => submission.id, :dir => up_or_down, :uh => modhash}, :headers => base_headers})
|
26
|
+
if resp.code == 200
|
27
|
+
return true
|
28
|
+
else
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby_reddit_api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 9
|
9
|
+
version: 0.1.9
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- James Cook
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-10-05 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: httparty
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: cucumber
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
description: Wraps many reddit API functions such as submission and comments browsing, voting, messaging, friending, and more.
|
47
|
+
email:
|
48
|
+
- jamecook@gmail.com
|
49
|
+
executables: []
|
50
|
+
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
extra_rdoc_files: []
|
54
|
+
|
55
|
+
files:
|
56
|
+
- lib/ruby_reddit_api.rb
|
57
|
+
- lib/ruby_reddit_api/user.rb
|
58
|
+
- lib/ruby_reddit_api/comment.rb
|
59
|
+
- lib/ruby_reddit_api/message.rb
|
60
|
+
- lib/ruby_reddit_api/version.rb
|
61
|
+
- lib/ruby_reddit_api/vote.rb
|
62
|
+
- lib/ruby_reddit_api/submission.rb
|
63
|
+
- lib/ruby_reddit_api/api.rb
|
64
|
+
- lib/ruby_reddit_api/json_listing.rb
|
65
|
+
- lib/ruby_reddit_api/base.rb
|
66
|
+
- README
|
67
|
+
- features/reddit.yml
|
68
|
+
- features/submission.feature
|
69
|
+
- features/comment.feature
|
70
|
+
- features/user.feature
|
71
|
+
- features/base.feature
|
72
|
+
- features/message.feature
|
73
|
+
- features/api.feature
|
74
|
+
- features/step_definitions/message_steps.rb
|
75
|
+
- features/step_definitions/submission_steps.rb
|
76
|
+
- features/step_definitions/api_steps.rb
|
77
|
+
- features/step_definitions/user_steps.rb
|
78
|
+
- features/step_definitions/comment_steps.rb
|
79
|
+
- features/step_definitions/base_steps.rb
|
80
|
+
- features/support/base_helpers.rb
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/jamescook/RubyRedditAPI
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
segments:
|
104
|
+
- 1
|
105
|
+
- 3
|
106
|
+
- 6
|
107
|
+
version: 1.3.6
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.3.7
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Wrapper for reddit API
|
115
|
+
test_files:
|
116
|
+
- features/reddit.yml
|
117
|
+
- features/submission.feature
|
118
|
+
- features/comment.feature
|
119
|
+
- features/user.feature
|
120
|
+
- features/base.feature
|
121
|
+
- features/message.feature
|
122
|
+
- features/api.feature
|
123
|
+
- features/step_definitions/message_steps.rb
|
124
|
+
- features/step_definitions/submission_steps.rb
|
125
|
+
- features/step_definitions/api_steps.rb
|
126
|
+
- features/step_definitions/user_steps.rb
|
127
|
+
- features/step_definitions/comment_steps.rb
|
128
|
+
- features/step_definitions/base_steps.rb
|
129
|
+
- features/support/base_helpers.rb
|