pushpop-product-hunt 0.1
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 +7 -0
- data/.gitignore +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/LICENSE +21 -0
- data/README.md +244 -0
- data/Rakefile +16 -0
- data/lib/pushpop-product-hunt.rb +112 -0
- data/lib/pushpop-product-hunt/client.rb +155 -0
- data/pushpop-plugin.gemspec +22 -0
- data/spec/lib/client_spec.rb +174 -0
- data/spec/pushpop-product-hunt_spec.rb +269 -0
- data/spec/spec_helper.rb +11 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f590d2284f5f7fec629133a55725070e7f3d21b4
|
4
|
+
data.tar.gz: c5d751a55023d8011c3fa2d7266678cb7fd072aa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d33cc2c7b46cb7edaa92c3d5592f47e33af91031947b23d0fac25af106845feca9e121e5a900eed8f9b709685052ec7c91dccae4aacc2592b87fc96ad7666778
|
7
|
+
data.tar.gz: 52cf34a62b6db21b7c85c17ff89394f2e76dde5cb2023c51265cc5535c7d6a36b40ee59432cbc2b169b044f446394e7472a857a6c1d54cb6ea468434930a06a8
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Author Josh Dzielak
|
2
|
+
Copyright (c) 2014 Keen Labs
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
# pushpop-product-hunt
|
2
|
+
|
3
|
+
Product Hunt plugin for [Pushpop](https://github.com/pushpop-project/pushpop).
|
4
|
+
|
5
|
+
- [Installation](#installation)
|
6
|
+
- [Usage](#usage)
|
7
|
+
- [Post Functions](#[post]-functions)
|
8
|
+
- [User Functions](#user-functions)
|
9
|
+
- [Collection Functions](#collection-functions)
|
10
|
+
- [Nested Resources](#nested-resources)
|
11
|
+
- [Pagination, Sorting, Ordering](#pagination-sorting-ordering)
|
12
|
+
- [Todo](#todo)
|
13
|
+
- [Contributing](#contributing)
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add `pushpop-product-hunt` to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'pushpop-product-hunt'
|
21
|
+
```
|
22
|
+
|
23
|
+
or install it as a gem
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ gem install pushpop-product-hunt
|
27
|
+
```
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
The product hunt plugin gives you an easy interface for pulling information out of Product Hunt. We've wrapped pretty much all of [Product Hunt's API](https://api.producthunt.com/v1/docs), so nearly everything is available to you.
|
32
|
+
|
33
|
+
Here's a quick preview of something a product hunt job could do
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
job 'alert me if Keen is on product hunt' do
|
37
|
+
product_hunt do
|
38
|
+
posts('https://keen.io')
|
39
|
+
end
|
40
|
+
|
41
|
+
step do |response|
|
42
|
+
if response['posts'].length > 0
|
43
|
+
response['posts']
|
44
|
+
else
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sendgrid do |posts|
|
50
|
+
# Send the email
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
In order to query the Product Hunt API, you will need to put an [application token](https://www.producthunt.com/v1/oauth/applications) in the `PRODUCT_HUNT_TOKEN` environment variable.
|
56
|
+
|
57
|
+
### Post Functions
|
58
|
+
|
59
|
+
**day([date])**
|
60
|
+
|
61
|
+
This requests all posts for a given day. By default, it will request posts from the current day.
|
62
|
+
|
63
|
+
Optionally, you can pass in a date specifier. Passing a number will get posts `N` days ago. Passing a string will attempt to parse that into a date, and request posts for that date.
|
64
|
+
|
65
|
+
``` ruby
|
66
|
+
product_hunt do
|
67
|
+
day # Get today's posts
|
68
|
+
end
|
69
|
+
|
70
|
+
product_hunt do
|
71
|
+
day 3 # Get posts from 3 days ago
|
72
|
+
end
|
73
|
+
|
74
|
+
product_hunt do
|
75
|
+
day '2014-07-04' # Get posts from July 4th, 2014.
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
**posts([url])**
|
80
|
+
|
81
|
+
This will get the most recent page of posts.
|
82
|
+
|
83
|
+
You can optionally pass in a URL to this function to filter to only posts that match that URL.
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
product_hunt do
|
87
|
+
posts # Get all posts
|
88
|
+
end
|
89
|
+
|
90
|
+
product_hunt do
|
91
|
+
posts 'https://keen.io' # Get posts on keen.io
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
**post(id)**
|
96
|
+
|
97
|
+
Gets a single post with the given ID
|
98
|
+
|
99
|
+
``` ruby
|
100
|
+
product_hunt do
|
101
|
+
post 1234 # Gets post 1234
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
### User Functions
|
106
|
+
|
107
|
+
**users()**
|
108
|
+
|
109
|
+
Gets all users
|
110
|
+
|
111
|
+
``` ruby
|
112
|
+
product_hunt do
|
113
|
+
users # Get all users
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
**user(id)**
|
118
|
+
|
119
|
+
Gets a user with the specified ID.
|
120
|
+
|
121
|
+
``` ruby
|
122
|
+
product_hunt do
|
123
|
+
user 4321 # Get user 4321
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
### Collection Functions
|
128
|
+
|
129
|
+
**collections()**
|
130
|
+
|
131
|
+
Get all collections
|
132
|
+
|
133
|
+
``` ruby
|
134
|
+
product_hunt do
|
135
|
+
collections # Get all collections
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
**featured_collections()**
|
140
|
+
|
141
|
+
Gets all of the featured collections.
|
142
|
+
|
143
|
+
``` ruby
|
144
|
+
product_hunt do
|
145
|
+
featured_collections # Get all featured collections
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
**collections(id)**
|
150
|
+
|
151
|
+
Gets the collection with the specified ID.
|
152
|
+
|
153
|
+
``` ruby
|
154
|
+
product_hunt do
|
155
|
+
collection 6789 # Get collection 6789
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
### Nested Resources
|
160
|
+
|
161
|
+
You can sometimes filter your results to only get resources _owned_ by another resource. In order to do that, you would ask for the parent resource first (ie: `user 10`), and then the child resource list (ie: `collections`).
|
162
|
+
|
163
|
+
#### Posts created by a user
|
164
|
+
|
165
|
+
``` ruby
|
166
|
+
product_hunt do
|
167
|
+
user 10
|
168
|
+
posts # Gets posts created by user 10
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
#### Collections created by a user
|
173
|
+
|
174
|
+
``` ruby
|
175
|
+
product_hunt do
|
176
|
+
user 10
|
177
|
+
collections # Gets collections created by user 10
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
#### Collections that contain a post
|
182
|
+
|
183
|
+
``` ruby
|
184
|
+
product_hunt do
|
185
|
+
post 35
|
186
|
+
collections # Gets collections that contain post 35
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
### Pagination, Sorting Ordering
|
191
|
+
|
192
|
+
Certain Product Hunt endpoints support pagination, sorting, and ordering. These functions allow you to customize your requests with those options.
|
193
|
+
|
194
|
+
**per_page(count)**
|
195
|
+
*available on posts, users, and collections.*
|
196
|
+
|
197
|
+
Sets the number of resources you should receive from any of the LIST functions
|
198
|
+
|
199
|
+
``` ruby
|
200
|
+
product_hunt do
|
201
|
+
posts
|
202
|
+
per_page 100
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
**newer_than(id)**
|
207
|
+
*available on posts, users, and collections.*
|
208
|
+
|
209
|
+
Filters your results to resources with an ID *higher* than the ID.
|
210
|
+
|
211
|
+
``` ruby
|
212
|
+
product_hunt do
|
213
|
+
posts
|
214
|
+
newer_than 1234
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
**older_than(id)**
|
219
|
+
*available on posts, users, and collections.*
|
220
|
+
|
221
|
+
Filters your results to resources with an ID *lower* than the ID.
|
222
|
+
|
223
|
+
``` ruby
|
224
|
+
product_hunt do
|
225
|
+
posts
|
226
|
+
older_than 4321
|
227
|
+
end
|
228
|
+
```
|
229
|
+
|
230
|
+
**sort(field, [direction])**
|
231
|
+
*available on collections*
|
232
|
+
|
233
|
+
Sorts your results by a certain field. By default, results will be sorted ascending. Pass `'desc'` as the second parameter to sort descending.
|
234
|
+
|
235
|
+
``` ruby
|
236
|
+
product_hunt do
|
237
|
+
collections
|
238
|
+
sort_by 'created_at', 'desc'
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
## Contributing
|
243
|
+
|
244
|
+
Code and documentation issues and pull requests are definitely welcome!
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$stdout.sync = true
|
2
|
+
|
3
|
+
$: << File.join(File.dirname(__FILE__), './lib')
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
desc 'Run Rspec unit tests'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: :spec
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'pushpop'
|
2
|
+
require 'pushpop-product-hunt/client'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
module Pushpop
|
6
|
+
module ProductHunt
|
7
|
+
class Step < Pushpop::Step
|
8
|
+
|
9
|
+
PLUGIN_NAME = 'product_hunt'
|
10
|
+
|
11
|
+
Pushpop::Job.register_plugin(PLUGIN_NAME, self)
|
12
|
+
|
13
|
+
## SETUP FUNCTIONS ##
|
14
|
+
|
15
|
+
def run(last_response=nil, step_responses=nil)
|
16
|
+
client.reset()
|
17
|
+
|
18
|
+
ret = self.configure(last_response, step_responses)
|
19
|
+
|
20
|
+
resp = client.get()
|
21
|
+
|
22
|
+
if resp
|
23
|
+
resp
|
24
|
+
else
|
25
|
+
ret
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure(last_response=nil, step_responses=nil)
|
30
|
+
self.instance_exec(last_response, step_responses, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def client
|
34
|
+
if @client
|
35
|
+
@client
|
36
|
+
else
|
37
|
+
if ENV['PRODUCT_HUNT_TOKEN'].nil? || ENV['PRODUCT_HUNT_TOKEN'].empty?
|
38
|
+
raise 'You have to set the PRODUCT_HUNT_TOKEN'
|
39
|
+
else
|
40
|
+
@client = Pushpop::ProductHunt::Client.new(ENV['PRODUCT_HUNT_TOKEN'])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
## QUERYING FUNCTIONS ##
|
46
|
+
|
47
|
+
def day(date = nil)
|
48
|
+
client.type('posts')
|
49
|
+
|
50
|
+
if date.is_a? String
|
51
|
+
# Parse the date, and then reoutput it to get a consistent format
|
52
|
+
client.option('day', Date.parse(date).to_s)
|
53
|
+
elsif date.is_a? Date
|
54
|
+
client.option('day', date.to_s)
|
55
|
+
elsif date.is_a? Numeric
|
56
|
+
client.option('days_ago', date)
|
57
|
+
elsif !date.nil?
|
58
|
+
raise 'Unknown date format'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def posts(url = nil)
|
63
|
+
client.identifier('all')
|
64
|
+
client.type('posts')
|
65
|
+
|
66
|
+
unless url.nil?
|
67
|
+
client.option('search[url]' => url)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def post(id)
|
72
|
+
client.post(id)
|
73
|
+
end
|
74
|
+
|
75
|
+
def users
|
76
|
+
client.type('users')
|
77
|
+
end
|
78
|
+
|
79
|
+
def user(id)
|
80
|
+
client.user(id)
|
81
|
+
end
|
82
|
+
|
83
|
+
def collections
|
84
|
+
client.type('collections')
|
85
|
+
end
|
86
|
+
|
87
|
+
def featured_collections
|
88
|
+
collections
|
89
|
+
client.option('search[featured]' => true)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Options Functions
|
93
|
+
|
94
|
+
def per_page(count)
|
95
|
+
client.option('per_page', count)
|
96
|
+
end
|
97
|
+
|
98
|
+
def older_than(max)
|
99
|
+
client.option('older', max)
|
100
|
+
end
|
101
|
+
|
102
|
+
def newer_than(min)
|
103
|
+
client.option('newer', min)
|
104
|
+
end
|
105
|
+
|
106
|
+
def sort(field, direction = 'asc')
|
107
|
+
client.option('sort_by', field)
|
108
|
+
client.option('order', direction)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Pushpop
|
5
|
+
module ProductHunt
|
6
|
+
class Client
|
7
|
+
include HTTParty
|
8
|
+
|
9
|
+
API_VERSION = 'v1'
|
10
|
+
base_uri 'https://api.producthunt.com'
|
11
|
+
|
12
|
+
USER_SCOPABLE_ENDPOINTS = [
|
13
|
+
'posts',
|
14
|
+
'collections'
|
15
|
+
]
|
16
|
+
POST_SCOPABLE_ENDPOINTS = [
|
17
|
+
'collections'
|
18
|
+
]
|
19
|
+
|
20
|
+
PAGINATING_ENDPOINTS = [
|
21
|
+
'posts',
|
22
|
+
'users',
|
23
|
+
'collections'
|
24
|
+
]
|
25
|
+
SORTABLE_ENDPOINTS = [
|
26
|
+
'collections'
|
27
|
+
]
|
28
|
+
ORDERABLE_ENDPOINTS = [
|
29
|
+
'users',
|
30
|
+
'collections'
|
31
|
+
]
|
32
|
+
|
33
|
+
attr_accessor :_user
|
34
|
+
attr_accessor :_post
|
35
|
+
attr_accessor :_options
|
36
|
+
|
37
|
+
def initialize(token)
|
38
|
+
@api_token = token
|
39
|
+
self._options = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def get
|
43
|
+
url = construct_url
|
44
|
+
|
45
|
+
if url
|
46
|
+
self.class.headers({
|
47
|
+
'Accept' => 'application/json',
|
48
|
+
'Content-Type' => 'application/json',
|
49
|
+
'Authorization' => "Bearer #{@api_token}"
|
50
|
+
})
|
51
|
+
|
52
|
+
response = self.class.get(url)
|
53
|
+
|
54
|
+
if response.code == 200
|
55
|
+
JSON.parse(response.body)
|
56
|
+
else
|
57
|
+
raise "Something went wrong with the Product Hunt request, returned status code #{response.code}"
|
58
|
+
end
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def reset
|
65
|
+
self._user = nil
|
66
|
+
self._post = nil
|
67
|
+
self._options = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def construct_url
|
71
|
+
return false unless @type
|
72
|
+
|
73
|
+
url = "/#{API_VERSION}/#{@type}"
|
74
|
+
|
75
|
+
if @identifier
|
76
|
+
url = "#{url}/#{@identifier}"
|
77
|
+
|
78
|
+
if @subtype
|
79
|
+
url = "#{url}/#{@subtype}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
url = "#{url}?"
|
84
|
+
escaper = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
85
|
+
self._options.each { |key, value|
|
86
|
+
case key
|
87
|
+
when 'older', 'newer', 'per_page'
|
88
|
+
next unless PAGINATING_ENDPOINTS.include?(@subtype || @type)
|
89
|
+
when 'sort_by'
|
90
|
+
next unless SORTABLE_ENDPOINTS.include?(@subtype || @type)
|
91
|
+
when 'order'
|
92
|
+
next unless ORDERABLE_ENDPOINTS.include?(@subtype || @type)
|
93
|
+
next unless ['asc', 'desc'].include?(value)
|
94
|
+
end
|
95
|
+
|
96
|
+
url = "#{url}#{URI.escape(key, escaper)}=#{URI.escape(value, escaper)}&"
|
97
|
+
}
|
98
|
+
|
99
|
+
url
|
100
|
+
end
|
101
|
+
|
102
|
+
def type(type)
|
103
|
+
@type = type
|
104
|
+
set_contextual_identifier
|
105
|
+
end
|
106
|
+
|
107
|
+
def subtype(type)
|
108
|
+
@subtype = type
|
109
|
+
end
|
110
|
+
|
111
|
+
def identifier(id)
|
112
|
+
@identifier = id
|
113
|
+
end
|
114
|
+
|
115
|
+
def user(id)
|
116
|
+
self._user = id
|
117
|
+
type('users')
|
118
|
+
identifier(id)
|
119
|
+
end
|
120
|
+
|
121
|
+
def post(id)
|
122
|
+
self._post = id
|
123
|
+
type('posts')
|
124
|
+
identifier(id)
|
125
|
+
end
|
126
|
+
|
127
|
+
def option(key, value)
|
128
|
+
self._options[key] = value
|
129
|
+
end
|
130
|
+
|
131
|
+
# If the user sets a specific post ID or user ID prior to calling
|
132
|
+
# one of the list functions like #collections, we should scope
|
133
|
+
# that call to only return items that are related
|
134
|
+
def set_contextual_identifier
|
135
|
+
# Let's prioritize user identifiers over posts identifiers
|
136
|
+
if self._user && USER_SCOPABLE_ENDPOINTS.include?(@type)
|
137
|
+
@subtype = @type
|
138
|
+
@type = 'users'
|
139
|
+
@identifier = self._user
|
140
|
+
|
141
|
+
true
|
142
|
+
elsif self._post && POST_SCOPABLE_ENDPOINTS.include?(@type)
|
143
|
+
@subtype = @type
|
144
|
+
@type = 'posts'
|
145
|
+
@identifier = self._post
|
146
|
+
|
147
|
+
true
|
148
|
+
else
|
149
|
+
false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
|
6
|
+
s.name = "pushpop-product-hunt"
|
7
|
+
s.version = '0.1'
|
8
|
+
s.authors = ["Joe Wegner"]
|
9
|
+
s.email = "joe@keen.io"
|
10
|
+
s.homepage = "https://github.com/pushpop-project/pushpop-product-hunt"
|
11
|
+
s.summary = "A Pushpop Plugin for triggering based on Product Hunt data"
|
12
|
+
|
13
|
+
s.add_dependency "pushpop"
|
14
|
+
s.add_dependency "hunting_season"
|
15
|
+
s.add_dependency 'httparty'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
|
4
|
+
describe Pushpop::ProductHunt::Client do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
stub_request(:get, /.*api\.producthunt\.com.*/)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'internal functions' do
|
11
|
+
client = nil
|
12
|
+
before(:each) do
|
13
|
+
client = Pushpop::ProductHunt::Client.new('12345')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'resets state values' do
|
17
|
+
client.user(123)
|
18
|
+
client.post(321)
|
19
|
+
client.option('number', 456)
|
20
|
+
|
21
|
+
client.reset
|
22
|
+
|
23
|
+
expect(client._user).to be_nil
|
24
|
+
expect(client._post).to be_nil
|
25
|
+
expect(client._options).to eq({})
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#set_contextual_identifier' do
|
29
|
+
it 'scopes user queries' do
|
30
|
+
client.user(10)
|
31
|
+
client.type('posts')
|
32
|
+
|
33
|
+
expect(client.instance_variable_get('@type')).to eq('users')
|
34
|
+
expect(client.instance_variable_get('@identifier')).to eq(10)
|
35
|
+
expect(client.instance_variable_get('@subtype')).to eq('posts')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'scopes post queries' do
|
39
|
+
client.post(10)
|
40
|
+
client.type('collections')
|
41
|
+
|
42
|
+
expect(client.instance_variable_get('@type')).to eq('posts')
|
43
|
+
expect(client.instance_variable_get('@identifier')).to eq(10)
|
44
|
+
expect(client.instance_variable_get('@subtype')).to eq('collections')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'doesnt scope thins that arent defined as scopable' do
|
48
|
+
client.user(10)
|
49
|
+
client.type('comments')
|
50
|
+
|
51
|
+
expect(client.instance_variable_get('@type')).to eq('comments')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#construct_url' do
|
56
|
+
before(:each) do
|
57
|
+
client.reset
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'builds URLs with types' do
|
61
|
+
client.type 'post'
|
62
|
+
|
63
|
+
expect(client.construct_url).to include('/post')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'builds URLs with identifiers' do
|
67
|
+
client.type 'post'
|
68
|
+
client.identifier 10
|
69
|
+
|
70
|
+
expect(client.construct_url).to include('/post/10')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'builds URLs with subtypes' do
|
74
|
+
client.type 'user'
|
75
|
+
client.identifier 10
|
76
|
+
client.subtype 'posts'
|
77
|
+
|
78
|
+
expect(client.construct_url).to include('/user/10/posts')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'puts options in the URL' do
|
82
|
+
client.type 'post'
|
83
|
+
client.option 'some', 'thing'
|
84
|
+
|
85
|
+
expect(client.construct_url).to include('some=thing')
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'doesnt add options that are blocked from an endpoint' do
|
89
|
+
client.type 'bad'
|
90
|
+
client.option 'order', 'asc'
|
91
|
+
|
92
|
+
expect(client.construct_url).not_to include('order=asc')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'query building functions' do
|
98
|
+
client = nil
|
99
|
+
before(:each) do
|
100
|
+
client = Pushpop::ProductHunt::Client.new('12345')
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'type' do
|
104
|
+
it 'sets the type of resource to request' do
|
105
|
+
client.type('test')
|
106
|
+
expect(client.instance_variable_get('@type')).to eq('test')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'will set contextual identifiers' do
|
110
|
+
expect(client).to receive(:set_contextual_identifier)
|
111
|
+
client.type('test')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe 'subtype' do
|
116
|
+
it 'sets the subtype to request' do
|
117
|
+
client.subtype('test')
|
118
|
+
expect(client.instance_variable_get('@subtype')).to eq('test')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'identifier' do
|
123
|
+
it 'sets the resource identifier to request' do
|
124
|
+
client.identifier(10)
|
125
|
+
expect(client.instance_variable_get('@identifier')).to eq(10)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'user' do
|
130
|
+
it 'sets the user context' do
|
131
|
+
client.user(10)
|
132
|
+
expect(client._user).to eq(10)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'sets the resource type to users' do
|
136
|
+
client.user(10)
|
137
|
+
expect(client.instance_variable_get('@type')).to eq('users')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'sets the resource identifier to the user id' do
|
141
|
+
client.user(10)
|
142
|
+
expect(client.instance_variable_get('@identifier')).to eq(10)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'post' do
|
147
|
+
it 'sets the post context' do
|
148
|
+
client.post(10)
|
149
|
+
expect(client._post).to eq(10)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'sets the resource type to posts' do
|
153
|
+
client.post(10)
|
154
|
+
expect(client.instance_variable_get('@type')).to eq('posts')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'sets the resource identifier to the post id' do
|
158
|
+
client.post(10)
|
159
|
+
expect(client.instance_variable_get('@identifier')).to eq(10)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe 'option' do
|
164
|
+
before(:each) do
|
165
|
+
client.reset
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'sets keys and values' do
|
169
|
+
client.option('test', 'tester')
|
170
|
+
expect(client._options['test']).to eq('tester')
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'date'
|
3
|
+
require 'webmock/rspec'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
ENV['PRODUCT_HUNT_TOKEN'] = '12345'
|
7
|
+
|
8
|
+
describe Pushpop::ProductHunt::Step do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
stub_request(:get, /.*api\.producthunt\.com.*/).
|
12
|
+
to_return(:body => JSON.generate({:success => true}))
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'internal functions' do
|
16
|
+
it 'has a ProductHunt::Client' do
|
17
|
+
step = Pushpop::ProductHunt::Step.new
|
18
|
+
|
19
|
+
expect(step.client).to be_a(Pushpop::ProductHunt::Client)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'resets the client' do
|
23
|
+
step = Pushpop::ProductHunt::Step.new do
|
24
|
+
#nothing
|
25
|
+
end
|
26
|
+
|
27
|
+
expect(step.client).to receive(:reset)
|
28
|
+
step.run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe '#day' do
|
34
|
+
it 'queries for posts by day' do
|
35
|
+
step = Pushpop::ProductHunt::Step.new do
|
36
|
+
day
|
37
|
+
end
|
38
|
+
|
39
|
+
expect(step.client).to receive(:type).with('posts')
|
40
|
+
|
41
|
+
step.run
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'queries for posts on a recent date' do
|
45
|
+
step = Pushpop::ProductHunt::Step.new do
|
46
|
+
day(3)
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(step.client).to receive(:option).with('days_ago', 3)
|
50
|
+
|
51
|
+
step.run
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'queries for posts for a specific date string' do
|
55
|
+
step = Pushpop::ProductHunt::Step.new do
|
56
|
+
day('15th May, 2015')
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(step.client).to receive(:option).with('day', '2015-05-15')
|
60
|
+
|
61
|
+
step.run
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'queries for posts for a Date object' do
|
65
|
+
step = Pushpop::ProductHunt::Step.new do
|
66
|
+
day(Date.parse('2015-05-15'))
|
67
|
+
end
|
68
|
+
|
69
|
+
expect(step.client).to receive(:option).with('day', '2015-05-15')
|
70
|
+
|
71
|
+
step.run
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#posts' do
|
76
|
+
it 'queries for all posts' do
|
77
|
+
step = Pushpop::ProductHunt::Step.new do
|
78
|
+
posts
|
79
|
+
end
|
80
|
+
|
81
|
+
expect(step.client).to receive(:type).with('posts')
|
82
|
+
expect(step.client).to receive(:identifier).with('all')
|
83
|
+
|
84
|
+
step.run
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'queries for posts with a specific URL' do
|
88
|
+
step = Pushpop::ProductHunt::Step.new do
|
89
|
+
posts 'https://www.example.com'
|
90
|
+
end
|
91
|
+
|
92
|
+
expect(step.client).to receive(:option).with('search[url]' => 'https://www.example.com')
|
93
|
+
|
94
|
+
step.run
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'queries for posts owned by a user' do
|
98
|
+
step = Pushpop::ProductHunt::Step.new do
|
99
|
+
user(10)
|
100
|
+
posts
|
101
|
+
end
|
102
|
+
|
103
|
+
step.run
|
104
|
+
|
105
|
+
expect(step.client.instance_variable_get('@type')).to eq('users')
|
106
|
+
expect(step.client.instance_variable_get('@identifier')).to eq(10)
|
107
|
+
expect(step.client.instance_variable_get('@subtype')).to eq('posts')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#post' do
|
112
|
+
it 'queries for a post by id' do
|
113
|
+
step = Pushpop::ProductHunt::Step.new do
|
114
|
+
post 10
|
115
|
+
end
|
116
|
+
|
117
|
+
expect(step.client).to receive(:post).with(10)
|
118
|
+
|
119
|
+
step.run
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'sets the current post context' do
|
123
|
+
step = Pushpop::ProductHunt::Step.new do
|
124
|
+
post 10
|
125
|
+
end
|
126
|
+
|
127
|
+
step.run
|
128
|
+
expect(step.client._post).to eq(10)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#users' do
|
133
|
+
it 'gets a list of users' do
|
134
|
+
step = Pushpop::ProductHunt::Step.new do
|
135
|
+
users
|
136
|
+
end
|
137
|
+
|
138
|
+
expect(step.client).to receive(:type).with('users')
|
139
|
+
|
140
|
+
step.run
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#user' do
|
145
|
+
it 'gets a single user' do
|
146
|
+
step = Pushpop::ProductHunt::Step.new do
|
147
|
+
user 10
|
148
|
+
end
|
149
|
+
|
150
|
+
expect(step.client).to receive(:user).with(10)
|
151
|
+
|
152
|
+
step.run
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'sets the current user context' do
|
156
|
+
step = Pushpop::ProductHunt::Step.new do
|
157
|
+
user 10
|
158
|
+
end
|
159
|
+
|
160
|
+
step.run
|
161
|
+
expect(step.client._user).to eq(10)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#collections' do
|
166
|
+
it 'gets all collections' do
|
167
|
+
step = Pushpop::ProductHunt::Step.new do
|
168
|
+
collections
|
169
|
+
end
|
170
|
+
|
171
|
+
expect(step.client).to receive(:type).with('collections')
|
172
|
+
|
173
|
+
step.run
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'queries for collections created by a user' do
|
177
|
+
step = Pushpop::ProductHunt::Step.new do
|
178
|
+
user(10)
|
179
|
+
collections
|
180
|
+
end
|
181
|
+
|
182
|
+
step.run
|
183
|
+
|
184
|
+
expect(step.client.instance_variable_get('@type')).to eq('users')
|
185
|
+
expect(step.client.instance_variable_get('@identifier')).to eq(10)
|
186
|
+
expect(step.client.instance_variable_get('@subtype')).to eq('collections')
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'queries for collections containing a post' do
|
190
|
+
step = Pushpop::ProductHunt::Step.new do
|
191
|
+
post(10)
|
192
|
+
collections
|
193
|
+
end
|
194
|
+
|
195
|
+
step.run
|
196
|
+
|
197
|
+
expect(step.client.instance_variable_get('@type')).to eq('posts')
|
198
|
+
expect(step.client.instance_variable_get('@identifier')).to eq(10)
|
199
|
+
expect(step.client.instance_variable_get('@subtype')).to eq('collections')
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#featured_collections' do
|
204
|
+
it 'gets the featured collections' do
|
205
|
+
step = Pushpop::ProductHunt::Step.new do
|
206
|
+
featured_collections
|
207
|
+
end
|
208
|
+
|
209
|
+
expect(step.client).to receive(:type).with('collections')
|
210
|
+
expect(step.client).to receive(:option).with('search[featured]' => true)
|
211
|
+
|
212
|
+
step.run
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe 'options functions' do
|
217
|
+
it 'sets the per page value' do
|
218
|
+
step = Pushpop::ProductHunt::Step.new do
|
219
|
+
per_page 100
|
220
|
+
end
|
221
|
+
|
222
|
+
expect(step.client).to receive(:option).with('per_page', 100)
|
223
|
+
|
224
|
+
step.run
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'sets the minimum id' do
|
228
|
+
step = Pushpop::ProductHunt::Step.new do
|
229
|
+
newer_than 54321
|
230
|
+
end
|
231
|
+
|
232
|
+
expect(step.client).to receive(:option).with('newer', 54321)
|
233
|
+
|
234
|
+
step.run
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'sets the maximum id' do
|
238
|
+
step = Pushpop::ProductHunt::Step.new do
|
239
|
+
older_than 98765
|
240
|
+
end
|
241
|
+
|
242
|
+
expect(step.client).to receive(:option).with('older', 98765)
|
243
|
+
|
244
|
+
step.run
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'sets the sort and defaults to asc' do
|
248
|
+
step = Pushpop::ProductHunt::Step.new do
|
249
|
+
sort 'created_at'
|
250
|
+
end
|
251
|
+
|
252
|
+
expect(step.client).to receive(:option).with('sort_by', 'created_at')
|
253
|
+
expect(step.client).to receive(:option).with('order', 'asc')
|
254
|
+
|
255
|
+
step.run
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'can be overridden to order by desc' do
|
259
|
+
step = Pushpop::ProductHunt::Step.new do
|
260
|
+
sort 'created_at', 'desc'
|
261
|
+
end
|
262
|
+
|
263
|
+
expect(step.client).to receive(:option).with('sort_by', 'created_at')
|
264
|
+
expect(step.client).to receive(:option).with('order', 'desc')
|
265
|
+
|
266
|
+
step.run
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pushpop-product-hunt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Wegner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pushpop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hunting_season
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
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: httparty
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email: joe@keen.io
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- .gitignore
|
62
|
+
- .travis.yml
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/pushpop-product-hunt.rb
|
68
|
+
- lib/pushpop-product-hunt/client.rb
|
69
|
+
- pushpop-plugin.gemspec
|
70
|
+
- spec/lib/client_spec.rb
|
71
|
+
- spec/pushpop-product-hunt_spec.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
homepage: https://github.com/pushpop-project/pushpop-product-hunt
|
74
|
+
licenses: []
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.0.14
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: A Pushpop Plugin for triggering based on Product Hunt data
|
96
|
+
test_files:
|
97
|
+
- spec/lib/client_spec.rb
|
98
|
+
- spec/pushpop-product-hunt_spec.rb
|
99
|
+
- spec/spec_helper.rb
|