├── .gitignore
├── .rspec
├── .rvmrc
├── Gemfile
├── Gemfile.lock
├── Procfile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── images
│ │ └── rails.png
│ ├── javascripts
│ │ └── application.js
│ └── stylesheets
│ │ ├── application.scss
│ │ └── base
│ │ ├── 960.scss
│ │ ├── global.scss
│ │ └── reset.scss
├── controllers
│ ├── application_controller.rb
│ └── home_controller.rb
├── helpers
│ └── application_helper.rb
├── mailers
│ └── .gitkeep
├── models
│ ├── .gitkeep
│ └── article.rb
└── views
│ ├── home
│ └── index.html.erb
│ └── layouts
│ └── application.html.erb
├── config.ru
├── config
├── application.rb
├── boot.rb
├── config.yml
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── backtrace_silencers.rb
│ ├── config.rb
│ ├── delayed_jobs.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── mongo.rb
│ ├── secret_token.rb
│ ├── session_store.rb
│ └── wrap_parameters.rb
├── locales
│ └── en.yml
└── routes.rb
├── db
├── migrate
│ └── 20111202224006_create_delayed_jobs.rb
├── schema.rb
└── seeds.rb
├── doc
└── README_FOR_APP
├── lib
├── delayed_jobs
│ └── process_new_articles_job.rb
└── tasks
│ ├── .gitkeep
│ ├── restart_processing.rake
│ ├── start_processing.rake
│ └── stop_processing.rake
├── log
└── .gitkeep
├── public
├── 404.html
├── 422.html
├── 500.html
├── favicon.ico
├── images
│ ├── backgrounds
│ │ ├── page_bg.png
│ │ ├── paper_bg.png
│ │ ├── paper_bottom.png
│ │ ├── paper_top.png
│ │ ├── soil.png
│ │ └── tree.png
│ ├── content
│ │ └── hook.png
│ └── icons
│ │ └── penguin.png
└── robots.txt
├── script
├── delayed_job
└── rails
├── spec
├── delayed_jobs
│ └── process_new_articles_job_spec.rb
├── models
│ └── article_spec.rb
└── spec_helper.rb
└── vendor
├── assets
└── stylesheets
│ └── .gitkeep
└── plugins
└── .gitkeep
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle
2 | db/*.sqlite3
3 | log/*.log
4 | tmp/
5 | .sass-cache/
6 | .DS_Store
7 | .idea
8 | coverage
9 | spec/coverage
10 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --colour
2 |
--------------------------------------------------------------------------------
/.rvmrc:
--------------------------------------------------------------------------------
1 | rvm use 1.9.2@rss2webhook
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'http://rubygems.org'
2 | source 'http://gemcutter.org'
3 |
4 | gem 'rails', '3.1.0'
5 |
6 | # Infrastructure
7 | gem 'pg'
8 | gem 'delayed_job'
9 | gem 'mongo_mapper'
10 | gem 'bson_ext'
11 | gem 'rest-client'
12 | gem 'thin'
13 |
14 | # Gems used only for assets and not required
15 | # in production environments by default.
16 | group :assets do
17 | gem 'coffee-rails', "~> 3.1.0"
18 | gem 'uglifier'
19 | end
20 |
21 | # Frontend
22 | gem 'sass-rails', "~> 3.1.0"
23 | gem 'jquery-rails'
24 | gem 'redcarpet'
25 |
26 | group :development, :test do
27 | gem 'annotate'
28 | gem 'rspec-rails'
29 | gem 'foreman'
30 | gem 'heroku'
31 | end
32 |
33 | group :test do
34 | # Pretty printed test output
35 | gem 'turn', :require => false
36 | gem 'simplecov', :require => false
37 | gem 'webmock'
38 | end
39 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | remote: http://gemcutter.org/
4 | specs:
5 | actionmailer (3.1.0)
6 | actionpack (= 3.1.0)
7 | mail (~> 2.3.0)
8 | actionpack (3.1.0)
9 | activemodel (= 3.1.0)
10 | activesupport (= 3.1.0)
11 | builder (~> 3.0.0)
12 | erubis (~> 2.7.0)
13 | i18n (~> 0.6)
14 | rack (~> 1.3.2)
15 | rack-cache (~> 1.0.3)
16 | rack-mount (~> 0.8.2)
17 | rack-test (~> 0.6.1)
18 | sprockets (~> 2.0.0)
19 | activemodel (3.1.0)
20 | activesupport (= 3.1.0)
21 | bcrypt-ruby (~> 3.0.0)
22 | builder (~> 3.0.0)
23 | i18n (~> 0.6)
24 | activerecord (3.1.0)
25 | activemodel (= 3.1.0)
26 | activesupport (= 3.1.0)
27 | arel (~> 2.2.1)
28 | tzinfo (~> 0.3.29)
29 | activeresource (3.1.0)
30 | activemodel (= 3.1.0)
31 | activesupport (= 3.1.0)
32 | activesupport (3.1.0)
33 | multi_json (~> 1.0)
34 | addressable (2.2.6)
35 | annotate (2.4.0)
36 | ansi (1.4.1)
37 | arel (2.2.1)
38 | bcrypt-ruby (3.0.1)
39 | bson (1.5.1)
40 | bson_ext (1.5.1)
41 | builder (3.0.0)
42 | coffee-rails (3.1.1)
43 | coffee-script (>= 2.2.0)
44 | railties (~> 3.1.0)
45 | coffee-script (2.2.0)
46 | coffee-script-source
47 | execjs
48 | coffee-script-source (1.1.3)
49 | crack (0.3.1)
50 | daemons (1.1.4)
51 | delayed_job (2.1.4)
52 | activesupport (~> 3.0)
53 | daemons
54 | diff-lcs (1.1.3)
55 | erubis (2.7.0)
56 | eventmachine (0.12.10)
57 | execjs (1.2.9)
58 | multi_json (~> 1.0)
59 | foreman (0.26.1)
60 | term-ansicolor (~> 1.0.5)
61 | thor (>= 0.13.6)
62 | heroku (2.15.0)
63 | launchy (>= 0.3.2)
64 | rest-client (~> 1.6.1)
65 | rubyzip
66 | term-ansicolor (~> 1.0.5)
67 | hike (1.2.1)
68 | i18n (0.6.0)
69 | jquery-rails (1.0.19)
70 | railties (~> 3.0)
71 | thor (~> 0.14)
72 | json (1.6.3)
73 | launchy (2.0.5)
74 | addressable (~> 2.2.6)
75 | mail (2.3.0)
76 | i18n (>= 0.4.0)
77 | mime-types (~> 1.16)
78 | treetop (~> 1.4.8)
79 | mime-types (1.17.2)
80 | mongo (1.5.1)
81 | bson (= 1.5.1)
82 | mongo_mapper (0.10.1)
83 | activemodel (~> 3.0)
84 | activesupport (~> 3.0)
85 | plucky (~> 0.4.0)
86 | multi_json (1.0.4)
87 | pg (0.11.0)
88 | plucky (0.4.3)
89 | mongo (~> 1.3)
90 | polyglot (0.3.3)
91 | rack (1.3.5)
92 | rack-cache (1.0.3)
93 | rack (>= 0.4)
94 | rack-mount (0.8.3)
95 | rack (>= 1.0.0)
96 | rack-ssl (1.3.2)
97 | rack
98 | rack-test (0.6.1)
99 | rack (>= 1.0)
100 | rails (3.1.0)
101 | actionmailer (= 3.1.0)
102 | actionpack (= 3.1.0)
103 | activerecord (= 3.1.0)
104 | activeresource (= 3.1.0)
105 | activesupport (= 3.1.0)
106 | bundler (~> 1.0)
107 | railties (= 3.1.0)
108 | railties (3.1.0)
109 | actionpack (= 3.1.0)
110 | activesupport (= 3.1.0)
111 | rack-ssl (~> 1.3.2)
112 | rake (>= 0.8.7)
113 | rdoc (~> 3.4)
114 | thor (~> 0.14.6)
115 | rake (0.9.2.2)
116 | rdoc (3.11)
117 | json (~> 1.4)
118 | redcarpet (2.0.0)
119 | rest-client (1.6.7)
120 | mime-types (>= 1.16)
121 | rspec (2.7.0)
122 | rspec-core (~> 2.7.0)
123 | rspec-expectations (~> 2.7.0)
124 | rspec-mocks (~> 2.7.0)
125 | rspec-core (2.7.1)
126 | rspec-expectations (2.7.0)
127 | diff-lcs (~> 1.1.2)
128 | rspec-mocks (2.7.0)
129 | rspec-rails (2.7.0)
130 | actionpack (~> 3.0)
131 | activesupport (~> 3.0)
132 | railties (~> 3.0)
133 | rspec (~> 2.7.0)
134 | rubyzip (0.9.5)
135 | sass (3.1.11)
136 | sass-rails (3.1.5)
137 | actionpack (~> 3.1.0)
138 | railties (~> 3.1.0)
139 | sass (~> 3.1.10)
140 | tilt (~> 1.3.2)
141 | simplecov (0.5.4)
142 | multi_json (~> 1.0.3)
143 | simplecov-html (~> 0.5.3)
144 | simplecov-html (0.5.3)
145 | sprockets (2.0.3)
146 | hike (~> 1.2)
147 | rack (~> 1.0)
148 | tilt (~> 1.1, != 1.3.0)
149 | term-ansicolor (1.0.7)
150 | thin (1.3.1)
151 | daemons (>= 1.0.9)
152 | eventmachine (>= 0.12.6)
153 | rack (>= 1.0.0)
154 | thor (0.14.6)
155 | tilt (1.3.3)
156 | treetop (1.4.10)
157 | polyglot
158 | polyglot (>= 0.3.1)
159 | turn (0.8.3)
160 | ansi
161 | tzinfo (0.3.31)
162 | uglifier (1.1.0)
163 | execjs (>= 0.3.0)
164 | multi_json (>= 1.0.2)
165 | webmock (1.7.8)
166 | addressable (~> 2.2, > 2.2.5)
167 | crack (>= 0.1.7)
168 |
169 | PLATFORMS
170 | ruby
171 |
172 | DEPENDENCIES
173 | annotate
174 | bson_ext
175 | coffee-rails (~> 3.1.0)
176 | delayed_job
177 | foreman
178 | heroku
179 | jquery-rails
180 | mongo_mapper
181 | pg
182 | rails (= 3.1.0)
183 | redcarpet
184 | rest-client
185 | rspec-rails
186 | sass-rails (~> 3.1.0)
187 | simplecov
188 | thin
189 | turn
190 | uglifier
191 | webmock
192 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec rails server -p $PORT thin -e $RACK_ENV
2 | worker: bundle exec rake jobs:work
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rss2webhook - Send RSS feed articles to any webhook
2 |
3 | rss2webhook is a barebones Rails app made to fetch new articles from an arbitrary
4 | RSS feed and POST (or GET) them to user-specified webhooks. It easily handles both plain
5 | and authenticated RSS feeds over http/https.
6 |
7 | The vast majority of the app's functionality can be configured through one YAML
8 | configuration file: ``config.yml``. RSS feeds are set up here with a URL and authentication
9 | options. The webhook can be configured to either POST or GET a custom JSON object.
10 | The JSON data to be sent is configurable to be any tree structure you like.
11 | ``|pipes|`` are used to inject RSS item attributes into the JSON. See the examples
12 | below for details.
13 |
14 | rss2webhook was made to be deployed easily on Heroku. You can however, tweak it to
15 | deploy on any server with a SQL-DB backing and MongoDB support.
16 |
17 | ## Deploying rss2webhook
18 |
19 | Below are instructions for deploying rss2webhook on Heroku.
20 |
21 | ### Step 1: Clone this repo
22 |
23 | git clone git://github.com/jookyboi/rss2webhook.git
24 |
25 | ### Step 2: Bundle the gems
26 |
27 | bundle install
28 |
29 | ### Step 3: Configure the Postgres database
30 |
31 | All database configuration is in ``config/database.yml``. Change it to suit your environment.
32 |
33 | ### Step 4: Create and Migrate database
34 |
35 | rake db:create
36 | rake db:migrate
37 |
38 | ### Step 5: Configure MongoMapper
39 |
40 | rss2webhook uses MongoMapper as an adapter for MongoDB. If you are testing the app out
41 | in your local environment and you already have MongoDB running, the first condition
42 | in ``config/initializers/mongo.rb`` should have you covered. If you don't have MongoDB installed,
43 | download the appropriate distro [here](http://www.mongodb.org/downloads).
44 |
45 | If you are deploying to Heroku, you'll need to sign up for either a [MongoLab](http://addons.heroku.com/mongolab)
46 | or [MongoHQ](http://addons.heroku.com/mongohq) account after deploying. (I personally use MongoLab as their
47 | free plan comes with a generous 240MB of space.) In ``mongo.rb``, uncomment one of the 2 configuration
48 | lines to work with your MongoDB provider.
49 |
50 | ```ruby
51 | MongoMapper.config = { Rails.env => {'uri' => ENV['MONGOLAB_URI']} }
52 | ```
53 |
54 | or for MongoHQ:
55 |
56 | ```ruby
57 | MongoMapper.config = { Rails.env => {'uri' => ENV['MONGOHQ_URL']} }
58 | ```
59 |
60 | ### Step 6: Configure your RSS feeds and webhooks
61 |
62 | Open up ``config/config.yml``, the central [YAML](http://www.yaml.org/) file for configuring
63 | rss2webhook.
64 |
65 | You'll need to start by adding sections for ``development:`` and ``production:``. For each section,
66 | define a set of global settings. Below is an example of one section for ``production``. Be sure to
67 | do something similar for ``development`` so you can test locally.
68 |
69 | ```yaml
70 | production:
71 | settings:
72 | type: post # either post or get
73 | process_on_start: false # whether to send all the articles on first fetch of the feed
74 | check_interval: 10 # check for feed updates every x seconds
75 | ```
76 |
77 | After that, you need to configure at least one RSS feed and its corresponding webhook. Here is
78 | an example for an unauthenticated RSS sample feed from [SilverOrange](http://labs.silverorange.com/archive/2003/july/privaterss)
79 | sending data to a HipChat room webhook ([message API](https://www.hipchat.com/docs/api/method/rooms/message)).
80 |
81 | ```yaml
82 | rss_feeds:
83 | -
84 | connection: http://labs.silverorange.com/local/solabs/rsstest/rss_plain.xml
85 | webhook: https://api.hipchat.com/v1/rooms/message # any web url
86 | type: get # can be get or post
87 | output:
88 | auth_token: 37b6805ad9ef28b523268053d5953c
89 | room_id: 48856
90 | from: |author| # equivalent to rss_article['author']
91 | message: |title| at |link| # interpolated values of rss_article
92 | format: json
93 | ```
94 |
95 | See the configuration examples below for info on dealing with basic authentication, SSL,
96 | and different output formats.
97 |
98 | ### Step 7: Test it out locally
99 |
100 | rss2webhook provides you with a few rake scripts to start, stop, and restart the RSS feed processing.
101 | Under the hood, they insert and delete delayed jobs.
102 |
103 | Assumbing your Mongo and DJ configuration is correct, you can insert the DJ for processing feeds with:
104 |
105 | rake start_processing
106 |
107 | Next, use [Foreman](http://michaelvanrooijen.com/articles/2011/06/08-managing-and-monitoring-your-ruby-application-with-foreman-and-upstart/)
108 | to spin up a worker:
109 |
110 | foreman start worker
111 |
112 | Assuming things are working, you should see the DJ fire once every few seconds for each one of the configured feeds.
113 |
114 | ### Step 8: Deploy on Heroku
115 |
116 | You are now ready to deploy on Heroku. Due to rss2webhook's reliance on a Procfile and Rails 3.1, it is
117 | recommended you use the [Cedar](http://devcenter.heroku.com/articles/cedar#using_cedar) stack.
118 |
119 | First, commit your changes:
120 |
121 | git commit -am "Changed configuration for deployment"
122 |
123 | In the project directory, type:
124 |
125 | heroku create --stack cedar
126 |
127 | Push your repo to Heroku:
128 |
129 | git push heroku master
130 |
131 | You'll need an instance of a MongoDB running. I recommend MongoLab. Their starter plan is free:
132 |
133 | heroku addons:add mongolab:starter
134 |
135 | Next, migrate the database:
136 |
137 | heroku run rake db:migrate
138 |
139 | Make sure you scale down the web worker (there is no frontend) and scale up the background worker:
140 |
141 | heroku scale web=0
142 | heroku scale worker=1
143 |
144 | Lastly, to kick everything off, run the rake task:
145 |
146 | heroku run rake start_processing
147 |
148 | Tail the logs just to make sure things are going as expected:
149 |
150 | heroku logs --tail
151 |
152 | That's it! You should now have an instance of rss2webhook sending RSS articles to webhooks.
153 |
154 | ## Configuration Examples
155 |
156 | Below are a few typical configurations.
157 |
158 | ### RSS over no-auth HTTP, simple POST to webhook
159 |
160 | ```yaml
161 | -
162 | connection: http://www.example.com/rss.xml
163 | webhook: http://www.chatroom.com/webhook # POSTs article => { link => ... }
164 | ```
165 |
166 | ### RSS over basic-auth HTTP, simple POST to webhook
167 |
168 | ```yaml
169 | -
170 | connection:
171 | host: basicauth.example.com
172 | ssl: false # for HTTPs, just set this to true
173 | resource: /feeds/daily
174 | auth:
175 | username: basicauth_user
176 | password: my_password
177 | webhook: http://www.chatroom.com/webhook
178 | ```
179 |
180 | ### Custom webhook data
181 |
182 | ```yaml
183 | -
184 | connection: http://www.example.com/rss.xml
185 | webhook: http://www.chatroom.com/webhook_custom
186 | output: # POSTs message => { summary => ..., content => ... }
187 | message:
188 | summary: |title|
189 | content: Full content - |description|
190 | ```
191 |
192 | ### Send data to webhook with GET request
193 |
194 | ```yaml
195 | -
196 | connection: http://www.example.com/rss.xml
197 | webhook: http://www.chatroom.com/webhook_get
198 | type: get
199 | ```
200 |
201 | ## Advanced Usage
202 |
203 | All the logic for rss2webhook can be found in the delayed job ``lib/delayed_jobs/process_new_articles_job.rb``.
204 | The ``perform`` method downloads the entire RSS feed and parses articles using the ``RSS::Parser`` lib. It then
205 | checks for new RSS items depending whether any of the newly fetched articles have urls which match articles
206 | already stored in MongoDB. If not, ``call_webhook`` is invoked for each of the new articles. If you have a need
207 | to use a different article field not officially specified in the RSS 2.0 standard (i.e. ``pubDate``), ``perform`` is the place
208 | to change it.
209 |
210 | rss2webhook performs very basic interpolation of RSS article fields in the output sent to webhooks. If
211 | for some reason the ``|`` pipe symbol is not working well for your content, the place to change it is in
212 | ``interpolate_output_with_values``. Simply change the ``regex`` specified to be what you need.
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rake
2 | # Add your own tasks in files placed in lib/tasks ending in .rake,
3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4 |
5 | require File.expand_path('../config/application', __FILE__)
6 |
7 | Rss2webhook::Application.load_tasks
8 |
--------------------------------------------------------------------------------
/app/assets/images/rails.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/app/assets/images/rails.png
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into including all the files listed below.
2 | // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3 | // be included in the compiled file accessible from http://example.com/assets/application.js
4 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5 | // the compiled file.
6 | //
7 | //= require jquery
8 | //= require jquery_ujs
9 | //= require_tree .
10 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | /* Base */
2 | @import 'base/reset';
3 | @import 'base/960';
4 | @import 'base/global';
--------------------------------------------------------------------------------
/app/assets/stylesheets/base/960.scss:
--------------------------------------------------------------------------------
1 | /*
2 | Variable Grid System.
3 | Learn more ~ http://www.spry-soft.com/grids/
4 | Based on 960 Grid System - http://960.gs/
5 |
6 | Licensed under GPL and MIT.
7 | */
8 |
9 | /*
10 | Forces backgrounds to span full width,
11 | even if there is horizontal scrolling.
12 | Increase this if your layout is wider.
13 |
14 | Note: IE6 works fine without this fix.
15 | */
16 |
17 | body {
18 | min-width: 660px;
19 | }
20 |
21 | /* Containers
22 | ----------------------------------------------------------------------------------------------------*/
23 | .container_12 {
24 | margin-left: auto;
25 | margin-right: auto;
26 | width: 660px;
27 | }
28 |
29 | /* Grid >> Global
30 | ----------------------------------------------------------------------------------------------------*/
31 |
32 |
33 | .grid_1,
34 | .grid_2,
35 | .grid_3,
36 | .grid_4,
37 | .grid_5,
38 | .grid_6,
39 | .grid_7,
40 | .grid_8,
41 | .grid_9,
42 | .grid_10,
43 | .grid_11,
44 | .grid_12 {
45 | display:inline;
46 | float: left;
47 | position: relative;
48 | margin-left: 5px;
49 | margin-right: 5px;
50 | }
51 |
52 |
53 |
54 | .push_1, .pull_1,
55 | .push_2, .pull_2,
56 | .push_3, .pull_3,
57 | .push_4, .pull_4,
58 | .push_5, .pull_5,
59 | .push_6, .pull_6,
60 | .push_7, .pull_7,
61 | .push_8, .pull_8,
62 | .push_9, .pull_9,
63 | .push_10, .pull_10,
64 | .push_11, .pull_11,
65 | .push_12, .pull_12 {
66 | position:relative;
67 | }
68 |
69 |
70 | /* Grid >> Children (Alpha ~ First, Omega ~ Last)
71 | ----------------------------------------------------------------------------------------------------*/
72 |
73 | .alpha {
74 | margin-left: 0;
75 | }
76 |
77 | .omega {
78 | margin-right: 0;
79 | }
80 |
81 | /* Grid >> 12 Columns
82 | ----------------------------------------------------------------------------------------------------*/
83 |
84 |
85 | .container_12 .grid_1 {
86 | width:45px;
87 | }
88 |
89 | .container_12 .grid_2 {
90 | width:100px;
91 | }
92 |
93 | .container_12 .grid_3 {
94 | width:155px;
95 | }
96 |
97 | .container_12 .grid_4 {
98 | width:210px;
99 | }
100 |
101 | .container_12 .grid_5 {
102 | width:265px;
103 | }
104 |
105 | .container_12 .grid_6 {
106 | width:320px;
107 | }
108 |
109 | .container_12 .grid_7 {
110 | width:375px;
111 | }
112 |
113 | .container_12 .grid_8 {
114 | width:430px;
115 | }
116 |
117 | .container_12 .grid_9 {
118 | width:485px;
119 | }
120 |
121 | .container_12 .grid_10 {
122 | width:540px;
123 | }
124 |
125 | .container_12 .grid_11 {
126 | width:595px;
127 | }
128 |
129 | .container_12 .grid_12 {
130 | width:650px;
131 | }
132 |
133 |
134 |
135 |
136 | /* Prefix Extra Space >> 12 Columns
137 | ----------------------------------------------------------------------------------------------------*/
138 |
139 |
140 | .container_12 .prefix_1 {
141 | padding-left:55px;
142 | }
143 |
144 | .container_12 .prefix_2 {
145 | padding-left:110px;
146 | }
147 |
148 | .container_12 .prefix_3 {
149 | padding-left:165px;
150 | }
151 |
152 | .container_12 .prefix_4 {
153 | padding-left:220px;
154 | }
155 |
156 | .container_12 .prefix_5 {
157 | padding-left:275px;
158 | }
159 |
160 | .container_12 .prefix_6 {
161 | padding-left:330px;
162 | }
163 |
164 | .container_12 .prefix_7 {
165 | padding-left:385px;
166 | }
167 |
168 | .container_12 .prefix_8 {
169 | padding-left:440px;
170 | }
171 |
172 | .container_12 .prefix_9 {
173 | padding-left:495px;
174 | }
175 |
176 | .container_12 .prefix_10 {
177 | padding-left:550px;
178 | }
179 |
180 | .container_12 .prefix_11 {
181 | padding-left:605px;
182 | }
183 |
184 |
185 |
186 | /* Suffix Extra Space >> 12 Columns
187 | ----------------------------------------------------------------------------------------------------*/
188 |
189 |
190 | .container_12 .suffix_1 {
191 | padding-right:55px;
192 | }
193 |
194 | .container_12 .suffix_2 {
195 | padding-right:110px;
196 | }
197 |
198 | .container_12 .suffix_3 {
199 | padding-right:165px;
200 | }
201 |
202 | .container_12 .suffix_4 {
203 | padding-right:220px;
204 | }
205 |
206 | .container_12 .suffix_5 {
207 | padding-right:275px;
208 | }
209 |
210 | .container_12 .suffix_6 {
211 | padding-right:330px;
212 | }
213 |
214 | .container_12 .suffix_7 {
215 | padding-right:385px;
216 | }
217 |
218 | .container_12 .suffix_8 {
219 | padding-right:440px;
220 | }
221 |
222 | .container_12 .suffix_9 {
223 | padding-right:495px;
224 | }
225 |
226 | .container_12 .suffix_10 {
227 | padding-right:550px;
228 | }
229 |
230 | .container_12 .suffix_11 {
231 | padding-right:605px;
232 | }
233 |
234 |
235 |
236 | /* Push Space >> 12 Columns
237 | ----------------------------------------------------------------------------------------------------*/
238 |
239 |
240 | .container_12 .push_1 {
241 | left:55px;
242 | }
243 |
244 | .container_12 .push_2 {
245 | left:110px;
246 | }
247 |
248 | .container_12 .push_3 {
249 | left:165px;
250 | }
251 |
252 | .container_12 .push_4 {
253 | left:220px;
254 | }
255 |
256 | .container_12 .push_5 {
257 | left:275px;
258 | }
259 |
260 | .container_12 .push_6 {
261 | left:330px;
262 | }
263 |
264 | .container_12 .push_7 {
265 | left:385px;
266 | }
267 |
268 | .container_12 .push_8 {
269 | left:440px;
270 | }
271 |
272 | .container_12 .push_9 {
273 | left:495px;
274 | }
275 |
276 | .container_12 .push_10 {
277 | left:550px;
278 | }
279 |
280 | .container_12 .push_11 {
281 | left:605px;
282 | }
283 |
284 |
285 |
286 | /* Pull Space >> 12 Columns
287 | ----------------------------------------------------------------------------------------------------*/
288 |
289 |
290 | .container_12 .pull_1 {
291 | left:-55px;
292 | }
293 |
294 | .container_12 .pull_2 {
295 | left:-110px;
296 | }
297 |
298 | .container_12 .pull_3 {
299 | left:-165px;
300 | }
301 |
302 | .container_12 .pull_4 {
303 | left:-220px;
304 | }
305 |
306 | .container_12 .pull_5 {
307 | left:-275px;
308 | }
309 |
310 | .container_12 .pull_6 {
311 | left:-330px;
312 | }
313 |
314 | .container_12 .pull_7 {
315 | left:-385px;
316 | }
317 |
318 | .container_12 .pull_8 {
319 | left:-440px;
320 | }
321 |
322 | .container_12 .pull_9 {
323 | left:-495px;
324 | }
325 |
326 | .container_12 .pull_10 {
327 | left:-550px;
328 | }
329 |
330 | .container_12 .pull_11 {
331 | left:-605px;
332 | }
333 |
334 |
335 |
336 |
337 | /* `Clear Floated Elements
338 | ----------------------------------------------------------------------------------------------------*/
339 |
340 | /* http://sonspring.com/journal/clearing-floats */
341 |
342 | .clear {
343 | clear: both;
344 | display: block;
345 | overflow: hidden;
346 | visibility: hidden;
347 | width: 0;
348 | height: 0;
349 | }
350 |
351 | /* http://www.yuiblog.com/blog/2010/09/27/clearfix-reloaded-overflowhidden-demystified */
352 |
353 | .clearfix:before,
354 | .clearfix:after {
355 | content: '\0020';
356 | display: block;
357 | overflow: hidden;
358 | visibility: hidden;
359 | width: 0;
360 | height: 0;
361 | }
362 |
363 | .clearfix:after {
364 | clear: both;
365 | }
366 |
367 | /*
368 | The following zoom:1 rule is specifically for IE6 + IE7.
369 | Move to separate stylesheet if invalid CSS is a problem.
370 | */
371 |
372 | .clearfix {
373 | zoom: 1;
374 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/base/global.scss:
--------------------------------------------------------------------------------
1 | /* Make HTML 5 elements display block-level for consistent styling */
2 | header, nav, article, footer, address, section {
3 | display: block;
4 | }
5 |
6 | /* Colors */
7 | $white: white;
8 | $brown: #978e7f;
9 | $light_paper: #faf8eb;
10 | $dark_brown: #231f20;
11 | $red_orange: #f7481b;
12 |
13 | /* Fonts */
14 | $monospace: anonymous-pro, 'Courier New', monospace;
15 |
16 | html {
17 | height: 100%;
18 | }
19 |
20 | body {
21 | background: url('/images/backgrounds/page_bg.png') $brown;
22 | font-family: myriad-pro, 'Helvetica Neue', Helvetica, Arial, sans-serif;
23 | min-height: 100%;
24 | }
25 |
26 | /* Text */
27 | a {
28 | text-decoration: none;
29 | }
30 |
31 | h1 {
32 | border-top: 5px solid $brown;
33 | padding-top: 10px;
34 | font-size: 25px;
35 | font-weight: bold;
36 | margin-bottom: 25px;
37 | }
38 |
39 | h2 {
40 | border-top: 2px solid $brown;
41 | padding-top: 10px;
42 | font-size: 20px;
43 | font-weight: bold;
44 | margin: 30px 0 20px 0;
45 | }
46 |
47 | h3 {
48 | font-size: 16px;
49 | font-weight: bold;
50 | margin: 25px 0 15px 0;
51 | }
52 |
53 | p {
54 | margin-bottom: 15px;
55 | }
56 |
57 | code {
58 | font-family: $monospace;
59 | }
60 |
61 | pre {
62 | font-family: $monospace;
63 | background: $brown;
64 | color: $white;
65 | padding: 4px 8px;
66 | font-size: 14px;
67 | line-height: 18px;
68 | margin-bottom: 15px;
69 | overflow: auto;
70 |
71 | box-shadow: inset 0 1px 4px $dark_brown;
72 | }
73 |
74 | /* Page elements */
75 | header {
76 | height: 117px;
77 |
78 | div.left {
79 | position: relative;
80 |
81 | div#hook {
82 | position: absolute;
83 | background: url('/images/content/hook.png') no-repeat;
84 | width: 59px;
85 | height: 177px;
86 |
87 | top: 0;
88 | left: 0;
89 | }
90 |
91 | div#name {
92 | position: absolute;
93 | font-size: 45px;
94 | color: $dark_brown;
95 | font-weight: 300;
96 | top: 45px;
97 | left: 30px;
98 |
99 | cursor: default;
100 | }
101 | }
102 |
103 | div.right {
104 | text-align: right;
105 | font-size: 25px;
106 | font-weight: 300;
107 | color: $white;
108 | padding-top: 62px;
109 |
110 | cursor: default;
111 | }
112 | }
113 |
114 | section.page {
115 | div.top_pattern {
116 | background: url('/images/backgrounds/paper_top.png') repeat-x;
117 | height: 48px;
118 | }
119 |
120 | div.content {
121 | background: url('/images/backgrounds/paper_bg.png');
122 | min-height: 300px;
123 | font-family: ff-tisa-web-pro, Georgia, serif;
124 | color: $dark_brown;
125 |
126 | padding: 60px 0;
127 | font-size: 16px;
128 | line-height: 24px;
129 |
130 | a {
131 | color: $red_orange;
132 | }
133 |
134 | a:hover {
135 | color: $dark_brown;
136 | }
137 |
138 | p code {
139 | font-size: 14px;
140 | }
141 | }
142 |
143 | div.bottom_pattern {
144 | background: url('/images/backgrounds/paper_bottom.png') repeat-x;
145 | height: 46px;
146 | }
147 | }
148 |
149 | footer {
150 | height: 240px;
151 | background: url('/images/backgrounds/soil.png') repeat-x 0% 100%;
152 |
153 | div.left {
154 | div#attrib {
155 | line-height: 200px;
156 | font-size: 15px;
157 | color: $light_paper;
158 | cursor: default;
159 | font-weight: 300;
160 |
161 | a {
162 | color: $dark_brown;
163 | }
164 |
165 | a:hover {
166 | color: $light_paper;
167 | }
168 |
169 | img {
170 | vertical-align: middle;
171 | margin: 0 8px 0 9px;
172 | }
173 | }
174 | }
175 |
176 | div.right {
177 | height: 232px;
178 | background: url('/images/backgrounds/tree.png') no-repeat 100% 100%;
179 | }
180 | }
--------------------------------------------------------------------------------
/app/assets/stylesheets/base/reset.scss:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 | Code licensed under the BSD License:
4 | http://developer.yahoo.com/yui/license.html
5 | version: 2.9.0
6 | */
7 | html{color:#000;background:#FFF}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,select,p,blockquote,th,td{margin:0;padding:0}table{border-collapse:collapse;border-spacing:0}fieldset,img{border:0}address,button,caption,cite,code,dfn,em,input,optgroup,option,select,strong,textarea,th,var{font:inherit}del,ins{text-decoration:none}li{list-style:none}caption,th{text-align:left}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}q:before,q:after{content:''}abbr,acronym{border:0;font-variant:normal}sup{vertical-align:baseline}sub{vertical-align:baseline}legend{color:#000}
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery
3 | end
4 |
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def index
3 | @markdown = Redcarpet::Markdown.new(
4 | Redcarpet::Render::HTML,
5 | :no_intra_emphasis => true,
6 | :fenced_code_blocks => true
7 | )
8 |
9 | @readme = File.open(File.join(Rails.root, 'README.md')).read
10 | end
11 | end
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/app/mailers/.gitkeep
--------------------------------------------------------------------------------
/app/models/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/app/models/.gitkeep
--------------------------------------------------------------------------------
/app/models/article.rb:
--------------------------------------------------------------------------------
1 | class Article
2 | include MongoMapper::Document
3 |
4 | # Written according to RSS 2.0 specs: http://cyber.law.harvard.edu/rss/rss.html
5 |
6 | # required
7 | key :title, String, :required => true
8 | key :link, String, :required => true
9 | key :description, String, :required => true
10 |
11 | # optional
12 | key :language, String
13 | key :copyright, String
14 | key :managingEditor, String
15 | key :webMaster, String
16 | key :pubDate, String
17 | key :lastBuildDate, Date
18 | key :category, String
19 | key :generator, String
20 | key :docs, String
21 | key :cloud, String
22 | key :ttl, Integer
23 | key :image, String
24 | key :rating, String
25 | key :textInput, String
26 | key :skipHours, Array
27 | key :skipDays, Array
28 |
29 | timestamps!
30 | end
--------------------------------------------------------------------------------
/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= @markdown.render(@readme).html_safe %>
3 |
4 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | rss2webook - open source Rails
5 | <%= stylesheet_link_tag "application" %>
6 | <%= javascript_include_tag "application" %>
7 | <%= csrf_meta_tags %>
8 |
9 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
55 |
56 |
57 |
58 |
59 |
60 | <%= yield %>
61 |
62 |
63 |
64 |
65 |
66 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run Rss2webhook::Application
5 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require 'rails/all'
4 |
5 | if defined?(Bundler)
6 | # If you precompile assets before deploying to production, use this line
7 | Bundler.require *Rails.groups(:assets => %w(development test))
8 | # If you want your assets lazily compiled in production, use this line
9 | # Bundler.require(:default, :assets, Rails.env)
10 | end
11 |
12 | module Rss2webhook
13 | class Application < Rails::Application
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration should go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded.
17 |
18 | # Custom directories with classes and modules you want to be autoloadable.
19 | config.autoload_paths += %W(#{config.root}/lib)
20 | config.autoload_paths += %W(#{config.root}/lib/delayed_jobs)
21 |
22 | # Only load the plugins named here, in the order given (default is alphabetical).
23 | # :all can be used as a placeholder for all plugins not explicitly named.
24 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
25 |
26 | # Activate observers that should always be running.
27 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer
28 |
29 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
30 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
31 | # config.time_zone = 'Central Time (US & Canada)'
32 |
33 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
34 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
35 | # config.i18n.default_locale = :de
36 |
37 | # Configure the default encoding used in templates for Ruby 1.9.
38 | config.encoding = "utf-8"
39 |
40 | # Configure sensitive parameters which will be filtered from the log file.
41 | config.filter_parameters += [:password]
42 |
43 | # Enable the asset pipeline
44 | config.assets.enabled = true
45 |
46 | # Version of your assets, change this if you want to expire all your assets
47 | config.assets.version = '1.0'
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | # Set up gems listed in the Gemfile.
4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5 |
6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
7 |
--------------------------------------------------------------------------------
/config/config.yml:
--------------------------------------------------------------------------------
1 | development:
2 | settings:
3 | process_on_start: false
4 | check_interval: 10 # in seconds
5 |
6 | rss_feeds:
7 | -
8 | connection:
9 | host: secure3.silverorange.com
10 | ssl: true
11 | resource: /rsstest/httpauth/rss_with_ssl_and_auth.xml
12 | auth:
13 | username: testuser
14 | password: testpass
15 |
16 | webhook: http://localhost:3011/hook
17 | output:
18 | room_id: 34343
19 | from: TMZ
20 | message: Body is |title|
21 | color: green
22 | format: json
23 |
24 | -
25 | connection: http://news.yahoo.com/rss/us
26 | webhook: http://localhost:3011/get_hook
27 | type: get
28 | output:
29 | article:
30 | from: Yahoo! News
31 | title: |title|
32 | link: |link|
33 |
34 | test:
35 | settings:
36 | process_on_start: false
37 | check_interval: 10 # in seconds
38 |
39 | rss_feeds:
40 | -
41 | connection: http://labs.silverorange.com/local/solabs/rsstest/rss_plain.xml
42 | webhook: http://example.com/webhook/post
43 |
44 | -
45 | connection: https://secure3.silverorange.com/rsstest/rss_with_ssl.xml
46 | webhook: http://example.com/webhook/get
47 | type: get
48 |
49 | -
50 | connection:
51 | host: labs.silverorange.com
52 | ssl: false
53 | resource: /local/solabs/rsstest/httpauth/rss_with_auth.xml
54 | auth:
55 | username: testuser
56 | password: testpass
57 | webhook: http://example.com/webhook/post_article
58 | output:
59 | article:
60 | title: My title is |title| from |link| with |description|
61 | link: |link|
62 |
63 | -
64 | connection:
65 | host: labs.silverorange.com
66 | ssl: true
67 | resource: /rsstest/httpauth/rss_with_ssl_and_auth.xml
68 | auth:
69 | username: testuser
70 | password: testpass
71 | webhook: http://example.com/webhook/post
72 |
73 | -
74 | connection:
75 | host: labs.silverorange.com
76 | ssl: true
77 | resource: /rsstest/httpauth/rss_with_ssl_and_auth.xml
78 | auth:
79 | username: testuser
80 | password: nonsense
81 | webhook: http://example.com/webhook/post
82 |
83 | -
84 | connection:
85 | host: labs.silverorange.com
86 | ssl: false
87 | resource: /rsstest/httpauth/rss_with_ssl_and_auth.xml
88 | auth:
89 | username: testuser
90 | password: testpass
91 | webhook: http://example.com/webhook/post
92 |
93 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: postgresql
3 | encoding: unicode
4 | database: rss2webhook_development
5 | pool: 5
6 | username: rss2webhook
7 | password: forme
8 |
9 | test:
10 | adapter: postgresql
11 | encoding: unicode
12 | database: rss2webhook_test
13 | pool: 5
14 | username: rss2webhook
15 | password: forme
16 |
17 | production:
18 | adapter: postgresql
19 | encoding: unicode
20 | database: rss2webhook_production
21 | pool: 5
22 | username: rss2webhook
23 | password: forme
24 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | Rss2webhook::Application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rss2webhook::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | config.action_mailer.raise_delivery_errors = false
18 |
19 | # Print deprecation notices to the Rails logger
20 | config.active_support.deprecation = :log
21 |
22 | # Only use best-standards-support built into browsers
23 | config.action_dispatch.best_standards_support = :builtin
24 |
25 | # Do not compress assets
26 | config.assets.compress = false
27 |
28 | # Expands the lines which load the assets
29 | config.assets.debug = true
30 | end
31 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rss2webhook::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # Code is not reloaded between requests
5 | config.cache_classes = true
6 |
7 | # Full error reports are disabled and caching is turned on
8 | config.consider_all_requests_local = false
9 | config.action_controller.perform_caching = true
10 |
11 | # Disable Rails's static asset server (Apache or nginx will already do this)
12 | config.serve_static_assets = false
13 |
14 | # Compress JavaScripts and CSS
15 | config.assets.compress = true
16 |
17 | # Don't fallback to assets pipeline if a precompiled asset is missed
18 | config.assets.compile = false
19 |
20 | # Generate digests for assets URLs
21 | config.assets.digest = true
22 |
23 | # Defaults to Rails.root.join("public/assets")
24 | # config.assets.manifest = YOUR_PATH
25 |
26 | # Specifies the header that your server uses for sending files
27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29 |
30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31 | # config.force_ssl = true
32 |
33 | # See everything in the log (default is :info)
34 | # config.log_level = :debug
35 |
36 | # Use a different logger for distributed setups
37 | # config.logger = SyslogLogger.new
38 |
39 | # Use a different cache store in production
40 | # config.cache_store = :mem_cache_store
41 |
42 | # Enable serving of images, stylesheets, and JavaScripts from an asset server
43 | # config.action_controller.asset_host = "http://assets.example.com"
44 |
45 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
46 | # config.assets.precompile += %w( search.js )
47 |
48 | # Disable delivery errors, bad email addresses will be ignored
49 | # config.action_mailer.raise_delivery_errors = false
50 |
51 | # Enable threaded mode
52 | # config.threadsafe!
53 |
54 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
55 | # the I18n.default_locale when a translation can not be found)
56 | config.i18n.fallbacks = true
57 |
58 | # Send deprecation notices to registered listeners
59 | config.active_support.deprecation = :notify
60 | end
61 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rss2webhook::Application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Configure static asset server for tests with Cache-Control for performance
11 | config.serve_static_assets = true
12 | config.static_cache_control = "public, max-age=3600"
13 |
14 | # Log error messages when you accidentally call methods on nil
15 | config.whiny_nils = true
16 |
17 | # Show full error reports and disable caching
18 | config.consider_all_requests_local = true
19 | config.action_controller.perform_caching = false
20 |
21 | # Raise exceptions instead of rendering exception templates
22 | config.action_dispatch.show_exceptions = false
23 |
24 | # Disable request forgery protection in test environment
25 | config.action_controller.allow_forgery_protection = false
26 |
27 | # Tell Action Mailer not to deliver emails to the real world.
28 | # The :test delivery method accumulates sent emails in the
29 | # ActionMailer::Base.deliveries array.
30 | config.action_mailer.delivery_method = :test
31 |
32 | # Use SQL instead of Active Record's schema dumper when creating the test database.
33 | # This is necessary if your schema can't be completely dumped by the schema dumper,
34 | # like if you have constraints or database-specific column types
35 | # config.active_record.schema_format = :sql
36 |
37 | # Print deprecation notices to the stderr
38 | config.active_support.deprecation = :stderr
39 |
40 | # Allow pass debug_assets=true as a query parameter to load pages with unpackaged assets
41 | config.assets.allow_debugging = true
42 | end
43 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/config.rb:
--------------------------------------------------------------------------------
1 | RSS_CONFIG = YAML.load_file(File.join(Rails.root, 'config', 'config.yml'))[Rails.env]
--------------------------------------------------------------------------------
/config/initializers/delayed_jobs.rb:
--------------------------------------------------------------------------------
1 | silence_warnings do
2 | Delayed::Job.const_set("MAX_ATTEMPTS", 1)
3 | Delayed::Job.const_set("MAX_RUN_TIME", 5.minutes)
4 | end
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format
4 | # (all these examples are active by default):
5 | # ActiveSupport::Inflector.inflections do |inflect|
6 | # inflect.plural /^(ox)$/i, '\1en'
7 | # inflect.singular /^(ox)en/i, '\1'
8 | # inflect.irregular 'person', 'people'
9 | # inflect.uncountable %w( fish sheep )
10 | # end
11 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 | # Mime::Type.register_alias "text/html", :iphone
6 |
--------------------------------------------------------------------------------
/config/initializers/mongo.rb:
--------------------------------------------------------------------------------
1 | if Rails.env == 'development' || Rails.env == 'test'
2 |
3 | MongoMapper.database = "rss2webhook_#{Rails.env}"
4 |
5 | elsif Rails.env == 'production'
6 |
7 | # Example MongoLab settings for Heroku
8 | #MongoMapper.config = { Rails.env => {'uri' => ENV['MONGOLAB_URI']} }
9 |
10 | # Example MongoHQ settings for Heroku
11 | #MongoMapper.config = { Rails.env => {'uri' => ENV['MONGOHQ_URL']} }
12 |
13 | MongoMapper.connect(Rails.env)
14 | end
15 |
--------------------------------------------------------------------------------
/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 | # Make sure the secret is at least 30 characters and all random,
6 | # no regular words or you'll be exposed to dictionary attacks.
7 | Rss2webhook::Application.config.secret_token = '971786c8ba03b4e57003f6770dab2e3d9d2565a6575ed91c64a5b77c4385a8316bdfa0f8ad1d399a1f949a7ab248be7a911b0f960bd269d5eee6ae999834b23e'
8 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rss2webhook::Application.config.session_store :cookie_store, key: '_rss2webhook_session'
4 |
5 | # Use the database for sessions instead of the cookie-based default,
6 | # which shouldn't be used to store highly confidential information
7 | # (create the session table with "rails generate session_migration")
8 | # Rss2webhook::Application.config.session_store :active_record_store
9 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # Disable root element in JSON by default.
12 | ActiveSupport.on_load(:active_record) do
13 | self.include_root_in_json = false
14 | end
15 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Sample localization file for English. Add more files in this directory for other locales.
2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3 |
4 | en:
5 | hello: "Hello world"
6 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rss2webhook::Application.routes.draw do
2 | root :to => 'home#index'
3 | end
4 |
--------------------------------------------------------------------------------
/db/migrate/20111202224006_create_delayed_jobs.rb:
--------------------------------------------------------------------------------
1 | class CreateDelayedJobs < ActiveRecord::Migration
2 | def self.up
3 | create_table :delayed_jobs, :force => true do |table|
4 | table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
5 | table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
6 | table.text :handler # YAML-encoded string of the object that will do work
7 | table.text :last_error # reason for last failure (See Note below)
8 | table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
9 | table.datetime :locked_at # Set when a client is working on this object
10 | table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
11 | table.string :locked_by # Who is working on this object (if locked)
12 | table.timestamps
13 | end
14 |
15 | add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
16 | end
17 |
18 | def self.down
19 | drop_table :delayed_jobs
20 | end
21 | end
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | # This file is auto-generated from the current state of the database. Instead
3 | # of editing this file, please use the migrations feature of Active Record to
4 | # incrementally modify your database, and then regenerate this schema definition.
5 | #
6 | # Note that this schema.rb definition is the authoritative source for your
7 | # database schema. If you need to create the application database on another
8 | # system, you should be using db:schema:load, not running all the migrations
9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10 | # you'll amass, the slower it'll run and the greater likelihood for issues).
11 | #
12 | # It's strongly recommended to check this file into your version control system.
13 |
14 | ActiveRecord::Schema.define(:version => 20111202224006) do
15 |
16 | create_table "delayed_jobs", :force => true do |t|
17 | t.integer "priority", :default => 0
18 | t.integer "attempts", :default => 0
19 | t.text "handler"
20 | t.text "last_error"
21 | t.datetime "run_at"
22 | t.datetime "locked_at"
23 | t.datetime "failed_at"
24 | t.string "locked_by"
25 | t.datetime "created_at"
26 | t.datetime "updated_at"
27 | end
28 |
29 | add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority"
30 |
31 | end
32 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
7 | # Mayor.create(name: 'Emanuel', city: cities.first)
8 |
--------------------------------------------------------------------------------
/doc/README_FOR_APP:
--------------------------------------------------------------------------------
1 | Use this README file to introduce your application and point to useful places in the API for learning more.
2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries.
3 |
--------------------------------------------------------------------------------
/lib/delayed_jobs/process_new_articles_job.rb:
--------------------------------------------------------------------------------
1 | require 'rss/2.0'
2 | require 'net/https'
3 |
4 | class ProcessNewArticlesJob < Struct.new(:rss_feed, :settings)
5 | def perform
6 | content = ''
7 |
8 | # support basic auth over ssl
9 | connection = rss_feed['connection']
10 |
11 | if connection['auth']
12 | content = fetch_rss_over_auth(rss_feed)
13 | else
14 | open(rss_feed['connection']) do |s|
15 | content = s.read
16 | end
17 | end
18 |
19 | # parse
20 | feed = RSS::Parser.parse(content, false)
21 |
22 | # all articles previously fetched
23 | feed_articles = Article.where(:feed_url => feed.channel.link)
24 | first_fetch = !feed_articles.any?
25 |
26 | # insert unique items into mongo
27 | feed.items.reverse.each do |item|
28 | item_hash = Hash.from_xml(item.to_s)['item']
29 | item_hash['feed_url'] = feed.channel.link
30 |
31 | # collection could change in the loop
32 | unless Article.where(:feed_url => feed.channel.link, :link => item_hash['link']).any?
33 |
34 | if !first_fetch || (first_fetch && settings['process_on_start'])
35 | call_webhook(item_hash, rss_feed)
36 | end
37 |
38 | # assume that items with different URLs are different
39 | article = Article.new(item_hash)
40 | article.save!
41 | end
42 | end
43 |
44 | schedule_next(settings['check_interval'])
45 | end
46 |
47 | private
48 |
49 | def fetch_rss_over_auth(rss_feed)
50 | connection = rss_feed['connection']
51 | port = 80
52 |
53 | if connection['ssl']
54 | port = 443
55 | end
56 |
57 | content = href = ''
58 | begin
59 | http = Net::HTTP.new(connection['host'], port)
60 | http.use_ssl = connection['ssl']
61 |
62 | http.start do |http|
63 | req = Net::HTTP::Get.new(connection['resource'])
64 | req.basic_auth(connection['auth']['username'], connection['auth']['password'])
65 |
66 | response = http.request(req)
67 | content = response.body
68 | end
69 | end
70 |
71 | content
72 | end
73 |
74 | def call_webhook(article_hash, rss_feed)
75 | webhook = rss_feed['webhook']
76 | output_settings = nil
77 |
78 | if rss_feed['output']
79 | output_settings = Marshal.load(Marshal.dump(rss_feed['output'])) # don't change the original
80 | end
81 |
82 | output_hash = Hash.new
83 |
84 | if output_settings
85 | # replace any placeholders
86 | interpolate_output_with_values(output_settings, article_hash)
87 | output_hash = output_settings
88 | else
89 | output_hash['article'] = article_hash.to_json
90 | end
91 |
92 | # post/get the request
93 | request_type = rss_feed['type']
94 | begin
95 | if request_type && request_type == 'get'
96 | RestClient.get webhook, output_hash
97 | else
98 | RestClient.post webhook, output_hash
99 | end
100 | rescue => e
101 | e.response
102 | end
103 | end
104 |
105 | def interpolate_output_with_values(node, article_hash)
106 | node.each do |k, v|
107 | if v.is_a? Hash
108 | interpolate_output_with_values(v, article_hash)
109 | else
110 | if v.is_a? String
111 | regex = /\|([A-Za-z0-9_]+)\|/i
112 | matches = regex.match v
113 |
114 | if matches
115 | node[k] = v.gsub(regex) do |s|
116 | eval "article_hash['#{$1}']"
117 | end
118 | end
119 | end
120 | end
121 | end
122 | end
123 |
124 | def schedule_next(check_interval)
125 | Delayed::Job.enqueue self, 0, Time.now + check_interval
126 | end
127 | end
--------------------------------------------------------------------------------
/lib/tasks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/lib/tasks/.gitkeep
--------------------------------------------------------------------------------
/lib/tasks/restart_processing.rake:
--------------------------------------------------------------------------------
1 | desc 'Restart processing new rss articles'
2 | task :restart_processing => :environment do
3 | # stop
4 | Delayed::Job.all.each do |dj|
5 | dj.delete
6 | end
7 |
8 | #start
9 | config = RSS_CONFIG
10 | feeds = config['rss_feeds']
11 |
12 | feeds.each do |feed|
13 | Delayed::Job.enqueue ProcessNewArticlesJob.new(feed, config['settings'])
14 | end
15 | end
--------------------------------------------------------------------------------
/lib/tasks/start_processing.rake:
--------------------------------------------------------------------------------
1 | desc 'Start processing new rss feed articles'
2 | task :start_processing => :environment do
3 | config = RSS_CONFIG
4 | feeds = config['rss_feeds']
5 |
6 | feeds.each do |feed|
7 | Delayed::Job.enqueue ProcessNewArticlesJob.new(feed, config['settings'])
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/tasks/stop_processing.rake:
--------------------------------------------------------------------------------
1 | desc 'Stop processing new rss articles'
2 | task :stop_processing => :environment do
3 | Delayed::Job.all.each do |dj|
4 | dj.delete
5 | end
6 | end
--------------------------------------------------------------------------------
/log/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/log/.gitkeep
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
We've been notified about this issue and we'll take a look at it shortly.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/backgrounds/page_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/page_bg.png
--------------------------------------------------------------------------------
/public/images/backgrounds/paper_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/paper_bg.png
--------------------------------------------------------------------------------
/public/images/backgrounds/paper_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/paper_bottom.png
--------------------------------------------------------------------------------
/public/images/backgrounds/paper_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/paper_top.png
--------------------------------------------------------------------------------
/public/images/backgrounds/soil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/soil.png
--------------------------------------------------------------------------------
/public/images/backgrounds/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/backgrounds/tree.png
--------------------------------------------------------------------------------
/public/images/content/hook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/content/hook.png
--------------------------------------------------------------------------------
/public/images/icons/penguin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/public/images/icons/penguin.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-Agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/script/delayed_job:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
4 | require 'delayed/command'
5 | Delayed::Command.new(ARGV).daemonize
6 |
--------------------------------------------------------------------------------
/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | APP_PATH = File.expand_path('../../config/application', __FILE__)
5 | require File.expand_path('../../config/boot', __FILE__)
6 | require 'rails/commands'
7 |
--------------------------------------------------------------------------------
/spec/delayed_jobs/process_new_articles_job_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe ProcessNewArticlesJob do
4 | before(:each) do
5 | Article.all.each do |a|
6 | a.delete
7 | end
8 |
9 | Delayed::Job.all.each do |job|
10 | job.delete
11 | end
12 |
13 | stub_requests
14 |
15 | @config = RSS_CONFIG
16 | @feeds = @config['rss_feeds']
17 | end
18 |
19 | describe 'Fetch RSS' do
20 | it 'should process simple unauthenticated rss' do
21 | enqueue_process(@feeds[0], @config['settings'])
22 | work_off
23 |
24 | item = Article.where(:link => 'http://labs.silverorange.com/archives/2003/june/canyousaythat')
25 | item.any?.should be(true)
26 | item.size.should be(1)
27 |
28 | # ensure all the properties are on the article
29 | article = item.first
30 | article.description.should_not be(nil)
31 | article.description.should_not be ""
32 |
33 | article.title.should_not be(nil)
34 | article.link.should match 'http://labs.silverorange.com/archives/2003/june/canyousaythat'
35 | article.guid.should_not be(nil)
36 | article.comments.should_not be(nil)
37 |
38 | Article.all.size.should be(6)
39 | end
40 |
41 | it 'should process rss over https' do
42 | enqueue_process(@feeds[1], @config['settings'])
43 | work_off
44 |
45 | Article.all.size.should be(6)
46 | end
47 |
48 | it 'should process authenticated rss over http' do
49 | enqueue_process(@feeds[2], @config['settings'])
50 | work_off
51 |
52 | Article.all.size.should be(6)
53 |
54 | item = Article.where(:link => 'http://labs.silverorange.com/archives/2003/june/introducingthe').first
55 | item.title.should match 'Introducing the silverorange Labs weblog'
56 | end
57 |
58 | it 'should process authenticated rss over https' do
59 | enqueue_process(@feeds[3], @config['settings'])
60 | work_off
61 |
62 | Article.all.size.should be(6)
63 | end
64 |
65 | it 'should not process incorrect auth over https' do
66 | enqueue_process(@feeds[4], @config['settings'])
67 | work_off
68 |
69 | Article.all.size.should be(0)
70 | end
71 |
72 | it 'should not process authenticated ssl over http' do
73 | enqueue_process(@feeds[5], @config['settings'])
74 | work_off
75 |
76 | Article.all.size.should be(0)
77 | end
78 | end
79 |
80 | describe 'Call webhook' do
81 | it 'should send correct article' do
82 | # full fetch
83 | enqueue_process(@feeds[0], @config['settings'])
84 | work_off
85 |
86 | Article.all.size.should be(6)
87 |
88 | # take one out
89 | link = 'http://labs.silverorange.com/archives/2003/june/introducingthe'
90 | Article.where(:link => link).first.delete
91 | Article.all.size.should be(5)
92 |
93 | Article.where(:link => link).any?.should be(false)
94 |
95 | # cause one to be sent to webhook
96 | enqueue_process(@feeds[0], @config['settings'])
97 | work_off
98 |
99 | Article.where(:link => link).any?.should be(true)
100 | Article.all.size.should be(6)
101 |
102 | WebMock.should have_requested(:get, 'labs.silverorange.com/local/solabs/rsstest/rss_plain.xml').twice
103 | WebMock.should have_requested(:post, 'example.com/webhook/post').with {
104 | |req| req.body.include?('params[article]') && req.body.include?('introducingthe')
105 | }
106 | end
107 |
108 | it 'should send correct article via get request' do
109 | enqueue_process(@feeds[1], @config['settings'])
110 | work_off
111 |
112 | link = 'http://labs.silverorange.com/archives/2003/june/photogallery'
113 | Article.where(:link => link).first.delete
114 | Article.all.size.should be(5)
115 |
116 | # cause one to be sent to webhook
117 | enqueue_process(@feeds[1], @config['settings'])
118 | work_off
119 |
120 | Article.where(:link => link).any?.should be(true)
121 | Article.all.size.should be(6)
122 |
123 | WebMock.should have_requested(:get, 'https://secure3.silverorange.com/rsstest/rss_with_ssl.xml').twice
124 | WebMock.should have_requested(:get, /.*example.com\/webhook\/get.*/).with {
125 | |req| req.uri.query.include?('photogallery')
126 | }
127 | end
128 |
129 | it 'should send correct article with proper output' do
130 | enqueue_process(@feeds[2], @config['settings'])
131 | work_off
132 |
133 | link = 'http://labs.silverorange.com/archives/2003/june/photogallery'
134 | Article.where(:link => link).first.delete
135 | Article.all.size.should be(5)
136 |
137 | # cause one to be sent to webhook
138 | enqueue_process(@feeds[2], @config['settings'])
139 | work_off
140 |
141 | Article.where(:link => link).any?.should be(true)
142 | Article.all.size.should be(6)
143 |
144 | WebMock.should have_requested(:get, 'http://testuser:testpass@labs.silverorange.com/local/solabs/rsstest/httpauth/rss_with_auth.xml').twice
145 | WebMock.should have_requested(:post, 'example.com/webhook/post_article').with {
146 | |req| req.body.include?('params[article][title]') && req.body.include?('params[article][link]') && req.body.include?('photogallery')
147 | }
148 | end
149 | end
150 |
151 | end
152 |
153 | private
154 |
155 | def stub_requests
156 | WebMock.reset!
157 |
158 | # allow fetches for RSS to pass right through
159 | WebMock.disable_net_connect!(:allow => ['labs.silverorange.com', 'secure3.silverorange.com'])
160 |
161 | stub_request(:get, /.*example.com\/webhook\/get.*/)
162 | stub_request(:post, 'example.com/webhook/post')
163 | stub_request(:post, 'example.com/webhook/post_article')
164 | end
165 |
166 | def enqueue_process(rss_feed, settings)
167 | Delayed::Job.enqueue ProcessNewArticlesJob.new(rss_feed, settings)
168 | end
169 |
170 | def work_off
171 | Delayed::Worker.new.work_off
172 | end
--------------------------------------------------------------------------------
/spec/models/article_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Article do
4 | before(:each) do
5 | Article.all.each do |a|
6 | a.delete
7 | end
8 | end
9 |
10 | it 'should let a valid article be saved' do
11 | article = Article.new(
12 | {
13 | :title => 'My new article',
14 | :link => 'http://www.google.com',
15 | :description => 'Test article'
16 | }
17 | )
18 |
19 | article.save!
20 | article.should have(:no).errors
21 | end
22 |
23 | it 'should not let invalid articles be saved' do
24 | article = Article.new(
25 | {
26 | :link => 'http://www.google.com',
27 | :description => 'Test article'
28 | }
29 | )
30 |
31 | article.save
32 | article.should have(1).error_on(:title)
33 |
34 | article = Article.new
35 | article.save
36 | article.should have(1).error_on(:title)
37 | article.should have(1).error_on(:link)
38 | article.should have(1).error_on(:description)
39 | end
40 | end
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'webmock/rspec'
2 | require 'simplecov'
3 |
4 | SimpleCov.start 'rails'
5 |
6 | # This file is copied to spec/ when you run 'rails generate rspec:install'
7 | ENV["RAILS_ENV"] ||= 'test'
8 | require File.expand_path("../../config/environment", __FILE__)
9 | require 'rspec/rails'
10 | require 'rspec/autorun'
11 |
12 | # Requires supporting ruby files with custom matchers and macros, etc,
13 | # in spec/support/ and its subdirectories.
14 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
15 |
16 | RSpec.configure do |config|
17 | config.mock_with :rspec
18 |
19 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
20 | config.fixture_path = "#{::Rails.root}/spec/fixtures"
21 |
22 | # If you're not using ActiveRecord, or you'd prefer not to run each of your
23 | # examples within a transaction, remove the following line or assign false
24 | # instead of true.
25 | config.use_transactional_fixtures = true
26 |
27 | # If true, the base class of anonymous controllers will be inferred
28 | # automatically. This will be the default behavior in future versions of
29 | # rspec-rails.
30 | config.infer_base_class_for_anonymous_controllers = false
31 | end
32 |
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/vendor/assets/stylesheets/.gitkeep
--------------------------------------------------------------------------------
/vendor/plugins/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jookyboi/rss2webhook/286950c9b14ac0aba847ed0bb42840a1816a19be/vendor/plugins/.gitkeep
--------------------------------------------------------------------------------