eventify 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1794610fc1c068a4f1d74d56f75bfab3ace97d6c
4
+ data.tar.gz: bbec3e34aeb04c91cc38316ef61abe58013af376
5
+ SHA512:
6
+ metadata.gz: 2ad9d63a20ebdca21135cafa59fc54facab5a2b0c365aac974a10b65a9051136d5f4aa32b40152a29bc1cbd363dcaa144a4f3084234f6fa7a052656bb0b88c3f
7
+ data.tar.gz: c44b7b19adc7f91199b5cd9051dc0502c8883b2eb164428ec6bc935a6d853026d6c619e118ce2c1864b11df0ecd6fd70aa04b2f7d7995bce83f837cb7ce789d2
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.log
2
+ /coverage/*
3
+ /pkg/*
4
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ rvm:
2
+ - 2.0.0
3
+ - 2.1.0
4
+ - ruby-head
5
+ notifications:
6
+ recipients:
7
+ - jarmo.p@gmail.com
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
11
+
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rufus-scheduler"
4
+
5
+ group :development do
6
+ gem "guard"
7
+ gem "guard-rspec"
8
+ gem "guard-bundler"
9
+ gem "wdm", platform: :mingw
10
+ gem "win32console", platform: :mingw
11
+ gem "ruby_gntp"
12
+ end
13
+
14
+ gemspec
15
+
16
+ gem "coveralls", :require => false
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ notification :ruby_gntp
2
+ #interactor :off
3
+
4
+ guard 'rspec', version: 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
10
+ guard 'bundler' do
11
+ watch('Gemfile')
12
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jarmo Pertman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Eventify
2
+ [![Gem Version](https://badge.fury.io/rb/eventify.png)](http://badge.fury.io/rb/eventify)
3
+ [![Build Status](https://api.travis-ci.org/jarmo/eventify.png)](http://travis-ci.org/jarmo/eventify)
4
+ [![Coverage](https://coveralls.io/repos/jarmo/eventify/badge.png?branch=master)](https://coveralls.io/r/jarmo/eventify)
5
+ [![Dependency Status](https://gemnasium.com/jarmo/eventify.png)](https://gemnasium.com/jarmo/eventify)
6
+ [![Code Climate](https://codeclimate.com/github/jarmo/eventify.png)](https://codeclimate.com/github/jarmo/eventify)
7
+
8
+ Are you tired of missing some cool events because you just didn't know them
9
+ happening? Are you tired of not getting good sitting places because you heard
10
+ of some event too late?
11
+
12
+ If the answer was yes to either of these questions then Eventify can help you!
13
+
14
+ Eventify will notify you about upcoming events from different
15
+ providers/organizers.
16
+
17
+ ## Installation
18
+
19
+ $ gem install eventify
20
+
21
+ ## Usage
22
+
23
+ Create configuration file:
24
+
25
+ $ ruby -reventify -e "Eventify::Configuration.new.save"
26
+
27
+ Edit configuration settings by adding your e-mail into `subscribers` list:
28
+
29
+ $ vi ~/.eventify/config.yaml
30
+
31
+ Run it from command line and add it into `cron`:
32
+
33
+ $ ruby -reventify -e "Eventify.new.process_new_events"
34
+
35
+ ## Supported Providers
36
+
37
+ The following providers are currently supported:
38
+
39
+ * [FBI](http://fbi.ee)
40
+ * [Piletilevi](http://www.piletilevi.ee/)
41
+ * [Ticketpro](http://www.ticketpro.ee/)
42
+
43
+ ## Adding New Providers
44
+
45
+ Adding new providers is easy. You just need to create a class with one method
46
+ satisfying the contract:
47
+
48
+ ```ruby
49
+ require "eventify"
50
+
51
+ class MyCustomProvider < Eventify::Provider::Base
52
+ def self.fetch
53
+ # fetch some atom feed
54
+ rss = SimpleRSS.parse open("http://example.org/rss.xml")
55
+ rss.entries.map { |entry| new id: entry.guid, title: entry.title, link: entry.link, date: entry.pubDate }
56
+ end
57
+ end
58
+
59
+ # use that provider with Eventify
60
+ eventify = Eventify.new
61
+ eventify.providers = [MyCustomProvider]
62
+ eventify.process_new_events
63
+ ```
64
+
65
+ ## License
66
+
67
+ See [LICENSE](https://github.com/jarmo/eventify/blob/master/LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ task :release => :spec
8
+
9
+ require "yard"
10
+ YARD::Rake::YardocTask.new
data/eventify.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'eventify/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "eventify"
8
+ spec.version = Eventify::VERSION
9
+ spec.authors = ["Jarmo Pertman"]
10
+ spec.email = ["jarmo.p@gmail.com"]
11
+ spec.description = %q{Get notifications about upcoming events from different providers/organizers.}
12
+ spec.summary = %q{Get notifications about upcoming events from different providers/organizers.}
13
+ spec.homepage = "https://github.com/jarmo/eventify"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "simple-rss"
22
+ spec.add_dependency "nokogiri"
23
+ spec.add_dependency "sqlite3"
24
+ spec.add_dependency "mail"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "webmock"
30
+ spec.add_development_dependency "yard"
31
+ end
data/lib/eventify.rb ADDED
@@ -0,0 +1,38 @@
1
+ require File.expand_path("eventify/version", __dir__)
2
+ require File.expand_path("eventify/configuration", __dir__)
3
+ require File.expand_path("eventify/provider/base", __dir__)
4
+ require File.expand_path("eventify/provider/fbi", __dir__)
5
+ require File.expand_path("eventify/provider/piletilevi", __dir__)
6
+ require File.expand_path("eventify/provider/ticketpro", __dir__)
7
+ require File.expand_path("eventify/database", __dir__)
8
+ require File.expand_path("eventify/mail", __dir__)
9
+
10
+ class Eventify
11
+ attr_reader :configuration
12
+
13
+ def initialize(configuration=Eventify::Configuration.new)
14
+ @configuration = configuration
15
+ end
16
+
17
+ def all_events
18
+ @all_events ||= providers.flat_map(&:fetch).uniq
19
+ end
20
+
21
+ def new_events
22
+ @new_events ||= all_events.reject(&:exists?)
23
+ end
24
+
25
+ def process_new_events
26
+ all_new_events = new_events
27
+ return if all_new_events.empty?
28
+
29
+ Eventify::Mail.deliver all_new_events, @configuration
30
+ all_new_events.each(&:save)
31
+ end
32
+
33
+ attr_writer :providers
34
+
35
+ def providers
36
+ @providers ||= [Eventify::Provider::Piletilevi, Eventify::Provider::Ticketpro, Eventify::Provider::FBI]
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require "yaml"
2
+
3
+ class Eventify::Configuration
4
+ PATH = File.expand_path("~/.eventify/config.yaml")
5
+
6
+ def initialize(configuration = {})
7
+ @configuration = default_configuration.merge(load).merge(configuration)
8
+ end
9
+
10
+ def save
11
+ File.open(PATH, "w") { |f| f.write YAML.dump(@configuration) }
12
+ end
13
+
14
+ def [](key)
15
+ @configuration[key]
16
+ end
17
+
18
+ private
19
+
20
+ def default_configuration
21
+ {
22
+ subscribers: ["user@example.org"],
23
+ mail: Mail.delivery_method.settings
24
+ }
25
+ end
26
+
27
+ def load
28
+ YAML.load(File.read(PATH)) rescue {}
29
+ end
30
+
31
+ end
@@ -0,0 +1,56 @@
1
+ require "sqlite3"
2
+
3
+ class Eventify::Database
4
+ PATH = File.expand_path("~/.eventify/events.sqlite3", __dir__)
5
+
6
+ class << self
7
+ def exists?(event)
8
+ results = sqlite.execute "select 1 from event where id=? and provider=? and link=?", event.id, event.provider, event.link
9
+ !results.empty?
10
+ end
11
+
12
+ def save(event)
13
+ sqlite.execute "insert into event values(?, ?, ?, ?, ?)",
14
+ event.id,
15
+ event.provider,
16
+ event.title,
17
+ event.link,
18
+ event.date.to_s
19
+ end
20
+
21
+ def events
22
+ translated_events = []
23
+ sqlite.execute("select * from event") do |event|
24
+ translated_events << const_get(event["provider"]).new(
25
+ id: event["id"],
26
+ title: event["title"],
27
+ link: event["link"],
28
+ date: Time.parse(event["date"])
29
+ )
30
+ end
31
+
32
+ translated_events
33
+ end
34
+
35
+ private
36
+
37
+ def sqlite
38
+ @sqlite ||= begin
39
+ FileUtils.mkdir_p File.dirname(PATH)
40
+ database = SQLite3::Database.new PATH
41
+ database.results_as_hash = true
42
+
43
+ database.execute "create table if not exists event(
44
+ id text,
45
+ provider text,
46
+ title text,
47
+ link text,
48
+ date text,
49
+ primary key (id, provider, link)
50
+ )"
51
+
52
+ database
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ require "mail"
2
+
3
+ class Eventify::Mail
4
+ class << self
5
+ def deliver(new_events, configuration)
6
+ formatted_events = format(new_events)
7
+
8
+ configuration[:subscribers].each do |subscriber|
9
+ ::Mail.deliver do
10
+ delivery_method :smtp, configuration[:mail]
11
+
12
+ content_type "text/plain; charset=utf-8"
13
+
14
+ to subscriber
15
+ from "no-reply@eventify.io"
16
+ subject "Event Rumours"
17
+ body formatted_events
18
+ end
19
+ end
20
+ end
21
+
22
+ def format(events)
23
+ header = "There are some rumours going on about #{events.size} possible awesome events:"
24
+
25
+ formatted_events = events.sort.reduce([header, ""]) do |memo, event|
26
+ memo << "* #{event.title}".force_encoding("UTF-8")
27
+ memo << " #{event.link}"
28
+ memo << ""
29
+ end
30
+
31
+ footer = "Your Humble Servant,\nEventify"
32
+
33
+ formatted_events << footer
34
+ formatted_events.join("\n")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ class Eventify
2
+ module Provider
3
+ class Base
4
+ include Comparable
5
+
6
+ class << self
7
+ def fetch
8
+ raise NotImplementedError
9
+ end
10
+ end
11
+
12
+ attr_reader :id, :title, :link, :date
13
+
14
+ def initialize(event)
15
+ @id = event[:id] or raise MissingAttributeError.new("id is missing")
16
+ @title = event[:title] or raise MissingAttributeError.new("title is missing")
17
+ @link = event[:link] or raise MissingAttributeError.new("link is missing")
18
+ @date = event[:date]
19
+ end
20
+
21
+ def provider
22
+ @provider ||= self.class.name
23
+ end
24
+
25
+ def save
26
+ Database.save self
27
+ self
28
+ end
29
+
30
+ def exists?
31
+ Database.exists? self
32
+ end
33
+
34
+ def ==(other)
35
+ id == other.id && provider == other.provider
36
+ end
37
+
38
+ alias_method :eql?, :==
39
+
40
+ def hash
41
+ "#{id}-#{provider}-#{link}".hash
42
+ end
43
+
44
+ def <=>(other)
45
+ title <=> other.title
46
+ end
47
+
48
+ MissingAttributeError = Class.new(RuntimeError)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ require "open-uri"
2
+ require "nokogiri"
3
+
4
+ module Eventify::Provider
5
+ class FBI < Base
6
+ URL = "http://www.fbi.ee/"
7
+
8
+ class << self
9
+ def fetch
10
+ doc = Nokogiri::HTML(open(URL))
11
+ doc.search(".article-list li h2 a").map do |a|
12
+ new id: a["href"], title: a.content, link: a["href"], date: Time.now
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ require "simple-rss"
2
+ require "open-uri"
3
+
4
+ module Eventify::Provider
5
+ class Piletilevi < Base
6
+ # http://www.piletilevi.ee/est/uldinfo/rss
7
+ URLS = [
8
+ "http://www.piletilevi.ee/news.rss.php?path=est/uudised",
9
+ "http://www.piletilevi.ee/news.rss.php?path=est/teatriuudised",
10
+ "http://www.piletilevi.ee/news.rss.php?path=est/muudatused",
11
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/muusika",
12
+ # 404?
13
+ #"http://www.piletilevi.ee/category.rss.php?path=est/piletid/teater_-_kunst",
14
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/kogupere",
15
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/sport",
16
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/festival",
17
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/film",
18
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/kinkekaardid",
19
+ "http://www.piletilevi.ee/category.rss.php?path=est/piletid/varia"
20
+ ]
21
+
22
+ class << self
23
+ def fetch
24
+ URLS.each.reduce([]) do |memo, url|
25
+ rss = SimpleRSS.parse open(url)
26
+ memo + rss.entries.map { |entry| new id: entry.guid, title: entry.title, link: entry.link, date: entry.pubDate }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end