herpes 0.0.1a1 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/herpes ADDED
@@ -0,0 +1,17 @@
1
+ #! /usr/bin/env ruby
2
+ require 'herpes'
3
+
4
+ if ARGV.first == '-v' or ARGV.first == '--version'
5
+ puts Herpes.version
6
+ exit
7
+ end
8
+
9
+ fail 'no configuration file passed' if ARGV.empty?
10
+
11
+ herpes = Herpes.load(*ARGV)
12
+
13
+ trap 'INT' do
14
+ herpes.stop!
15
+ end
16
+
17
+ herpes.start!
@@ -0,0 +1,11 @@
1
+ every 5.seconds do
2
+ dispatch {
3
+ time Time.now
4
+
5
+ comment time.to_i.to_s[-2].to_i.odd? ? 'O_O' : '^_^'
6
+ }
7
+ end
8
+
9
+ on :anything do |event|
10
+ puts "It is #{event.time} #{event.comment}"
11
+ end
data/examples/rss.rb CHANGED
@@ -1,26 +1,68 @@
1
+ require 'herpes/rss'
2
+ require 'herpes/email'
3
+
4
+ state '~/.herpes'
5
+
1
6
  # load the RSS module
2
7
  use :rss do
8
+ check_every 2.minutes
9
+
3
10
  # register sankaku with the following tags for the generated events
4
11
  tag :anime, :manga, :japan, :nsfw do
5
12
  register 'http://www.sankakucomplex.com/feed/'
6
13
  end
7
14
 
8
- # register incomaemeglio in the blog group
15
+ # register incomaemeglio in the blog group and give it a name
16
+ # also register github's blog in the blog group
9
17
  group :blog do
10
- register 'http://feeds.feedburner.com/incomaemeglio'
18
+ register 'http://feeds.feedburner.com/incomaemeglio', :smeriglia
19
+ register 'https://github.com/blog.atom'
11
20
  end
12
21
  end
13
22
 
14
- on -> e { e.tags.include?(:nsfw) } do |event|
15
- with :email do
16
- from 'rss-nsfw@events'
17
- to 'meh@paranoici.org'
18
- end.send(event)
19
- end
23
+ # only events coming from the RSS module
24
+ from :rss do
25
+ # define some common helper methods on every event
26
+ before do |event|
27
+ require 'nokogiri'
28
+
29
+ [event.channel.title, event.title, event.description].each {|obj|
30
+ class << obj
31
+ def strip_html
32
+ Nokogiri::HTML(self).search('//text()').text
33
+ end
34
+ end
35
+ }
36
+
37
+ class << event
38
+ def render
39
+ "#{title.strip_html} (#{link})\n\n#{description.strip_html}"
40
+ end
41
+ end
42
+ end
20
43
 
21
- on :anything_else do |event|
22
- with :email do
23
- from 'rss@events'
24
- to 'meh@paranoici.org'
25
- end.send(event)
44
+ # for events that have the nsfw tag
45
+ on -> e { e.tags.include?(:nsfw) } do |event|
46
+ with :email do
47
+ from "#{event.channel.title.strip_html} <rss-nsfw@herpes>"
48
+ to 'herpes'
49
+ subject event.title.strip_html
50
+
51
+ via :procmail
52
+
53
+ send event.render
54
+ end
55
+ end
56
+
57
+ on :anything_else do |event|
58
+ with :email do
59
+ from "#{event.channel.title.strip_html} <rss@herpes>"
60
+ to 'herpes'
61
+ subject event.title.strip_html
62
+
63
+ via :procmail
64
+
65
+ send event.render
66
+ end
67
+ end
26
68
  end
data/herpes.gemspec CHANGED
@@ -13,4 +13,6 @@ Gem::Specification.new {|s|
13
13
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
14
14
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
15
  s.require_paths = ['lib']
16
+
17
+ s.add_dependency 'actionpool'
16
18
  }
@@ -0,0 +1,55 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'pony'
12
+
13
+ Herpes::Notifier.define :email, :mail do
14
+ @options = %w(from to cc bcc sender subject headers charset text_part_charset message_id via via_options attachments)
15
+
16
+ plain_accessor *@options
17
+
18
+ from 'herpes'
19
+
20
+ @attachments = {}
21
+
22
+ def via (name, options = {})
23
+ if name == :procmail
24
+ procmail = `which procmail`.chomp
25
+ procmail = procmail.empty? ? '/usr/bin/procmail' : procmail
26
+
27
+ via :sendmail, location: "#{options[:location] || procmail} -f - #"
28
+ else
29
+ @via = name
30
+ @via_options = options
31
+ end
32
+ end
33
+
34
+ def attachment (name, path)
35
+ @attachments[name] = File.read(File.expand_path(path))
36
+ end
37
+
38
+ def to_hash
39
+ result = {}
40
+
41
+ @options.each {|name|
42
+ result[name.to_sym] = instance_variable_get("@#{name}") if instance_variable_get("@#{name}")
43
+ }
44
+
45
+ result
46
+ end
47
+
48
+ def send (data)
49
+ if !data.is_a?(Hash)
50
+ data = { :text => data.to_s }
51
+ end
52
+
53
+ Pony.mail(to_hash.merge(body: data[:text], html_body: data[:html]))
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ class Herpes
12
+
13
+ class Event
14
+ def initialize (&block)
15
+ @data = {}
16
+
17
+ instance_eval &block
18
+ end
19
+
20
+ def method_missing (id, *args)
21
+ id = id.to_s.sub(/[=?]$/, '').to_sym
22
+
23
+ if args.length == 0
24
+ @data[id]
25
+ else
26
+ if respond_to? "#{id}="
27
+ send "#{id}=", *args
28
+ else
29
+ value = (args.length > 1) ? args : args.first
30
+
31
+ if value.nil?
32
+ @data.delete(id)
33
+ else
34
+ @data[id] = value
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def to_hash
41
+ @data.dup
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,95 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'forwardable'
12
+
13
+ class Numeric
14
+ def seconds
15
+ self
16
+ end; alias second seconds
17
+
18
+ def minutes
19
+ self * 60
20
+ end; alias minute minutes
21
+
22
+ def hours
23
+ self * 60.minutes
24
+ end; alias hour hours
25
+
26
+ def days
27
+ self * 24.hours
28
+ end; alias day days
29
+ end
30
+
31
+ class Object
32
+ def plain_accessor (*names)
33
+ names.each {|name|
34
+ define_singleton_method name do |*args|
35
+ if args.empty?
36
+ instance_variable_get "@#{name}"
37
+ else
38
+ value = (args.length > 1) ? args : args.first
39
+
40
+ if value.nil?
41
+ remove_instance_variable "@#{name}"
42
+ else
43
+ instance_variable_set "@#{name}", value
44
+ end
45
+ end
46
+ end
47
+
48
+ define_singleton_method "#{name}?" do
49
+ instance_variable_get "@#{name}"
50
+ end
51
+
52
+ define_singleton_method "#{name}!" do
53
+ instance_variable_set "@#{name}", true
54
+ end
55
+
56
+ define_singleton_method "no_#{name}!" do
57
+ instance_variable_set "@#{name}", false
58
+ end
59
+
60
+ }
61
+ end
62
+ end
63
+
64
+ class Module
65
+ def plain_accessor (*names)
66
+ names.each {|name|
67
+ define_method name do |*args|
68
+ if args.empty?
69
+ instance_variable_get "@#{name}"
70
+ else
71
+ value = (args.length > 1) ? args : args.first
72
+
73
+ if value.nil?
74
+ remove_instance_variable "@#{name}"
75
+ else
76
+ instance_variable_set "@#{name}", value
77
+ end
78
+ end
79
+ end
80
+
81
+ define_method "#{name}?" do
82
+ instance_variable_get "@#{name}"
83
+ end
84
+
85
+ define_method "#{name}!" do
86
+ instance_variable_set "@#{name}", true
87
+ end
88
+
89
+ define_method "no_#{name}!" do
90
+ instance_variable_set "@#{name}", false
91
+ end
92
+
93
+ }
94
+ end
95
+ end
@@ -0,0 +1,129 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ class Herpes
12
+
13
+ class Module
14
+ def self.all
15
+ @modules ||= []
16
+ end
17
+
18
+ def self.[] (name)
19
+ return name if name.is_a?(Module)
20
+
21
+ Module.all.find { |mod| mod =~ name }
22
+ end
23
+
24
+ def self.define (name, *aliases, &block)
25
+ Module.all << new(name, *aliases, &block)
26
+ end
27
+
28
+ extend Forwardable
29
+
30
+ attr_reader :name, :aliases, :owner
31
+ def_delegators :owner, :state, :workers
32
+
33
+ def initialize (name, *aliases, &block)
34
+ @name = name
35
+ @aliases = aliases
36
+
37
+ instance_eval &block
38
+ end
39
+
40
+ def =~ (other)
41
+ return true if self == other
42
+
43
+ name.to_s.downcase == other.to_s.downcase || aliases.any? { |a| a.to_s.downcase == other.to_s.downcase }
44
+ end
45
+
46
+ def default (&block)
47
+ block ? @default : @default = block
48
+ end
49
+
50
+ def with (&block)
51
+ block = default unless block
52
+
53
+ if !block
54
+ raise ArgumentError, 'no block passed and a default is not present'
55
+ end
56
+
57
+ clone.tap { |o| o.instance_eval &block }
58
+ end
59
+
60
+ def use (*)
61
+ raise NotImplementedError, 'you have to use a specialized module'
62
+ end
63
+
64
+ def inspect
65
+ "#<#{self.class.name}(#{name}#{" [#{aliases.join ', '}]" unless aliases.empty?})>"
66
+ end
67
+ end
68
+
69
+ class Generator < Module
70
+ plain_accessor :check_every
71
+
72
+ def initialize (*)
73
+ super
74
+ end
75
+
76
+ def use (owner, &block)
77
+ with(&block).tap {|o|
78
+ o.instance_eval {
79
+ @owner = owner
80
+
81
+ owned if respond_to? :owned
82
+
83
+ if respond_to? :check
84
+ @owner.every(check_every, &method(:check))
85
+ end
86
+ }
87
+ }
88
+ end
89
+
90
+ def dispatch (event = nil, &block)
91
+ if block && !event
92
+ event = Event.new(&block)
93
+ end
94
+
95
+ raise ArgumentError, 'you did not pass an Event' unless event.is_a?(Event)
96
+
97
+ event.generated_by self
98
+
99
+ owner.dispatch(event)
100
+ end
101
+ end
102
+
103
+ class Notifier < Module
104
+ def initialize (*)
105
+ @matchers = []
106
+
107
+ super
108
+ end
109
+
110
+ def on (*args, &block)
111
+ @matchers << Struct.new(:arguments, :block).new(args, block)
112
+ end
113
+
114
+ def use (owner, &block)
115
+ with(&block).tap {|o|
116
+ o.instance_eval {
117
+ @owner = owner
118
+
119
+ owned if respond_to? :owned
120
+
121
+ @matchers.each {|matcher|
122
+ @owner.on *matcher.arguments, &matcher.block
123
+ }
124
+ }
125
+ }
126
+ end
127
+ end
128
+
129
+ end
data/lib/herpes/rss.rb ADDED
@@ -0,0 +1,88 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'rss'
12
+ require 'open-uri'
13
+
14
+ Herpes::Generator.define :rss do
15
+ plain_accessor :digest
16
+
17
+ check_every 5.minutes
18
+
19
+ @tags = []
20
+ @rss = []
21
+
22
+ def tag (*tags, &block)
23
+ @tags.push tags
24
+ instance_eval &block
25
+ @tags.pop
26
+ end
27
+
28
+ def group (group, &block)
29
+ @group, tmp = group, @group
30
+ instance_eval &block
31
+ @group = tmp
32
+ end
33
+
34
+ def register (url, name = nil)
35
+ @rss << Struct.new(:url, :name, :tags, :group).new(url, name, @tags.flatten, @group)
36
+ end
37
+
38
+ def check
39
+ digest = [] if digest?
40
+
41
+ @rss.each {|r|
42
+ (state[:rss] ||= {})[r.url] ||= []
43
+
44
+ RSS::Parser.parse(open(r.url).read, false).tap {|p|
45
+ p.items.each {|item|
46
+ next if state[:rss][r.url].member? [item.date, item.title]
47
+
48
+ event = Herpes::Event.new {
49
+ tags r.tags
50
+ group r.group
51
+ name r.name
52
+
53
+ channel p.channel.dup
54
+
55
+ title item.title.dup
56
+ link item.link.dup
57
+ description item.description.dup
58
+ date item.date
59
+ }
60
+
61
+ if digest
62
+ digest.push(event)
63
+ else
64
+ dispatch(event)
65
+ end
66
+
67
+ state[:rss][r.url].push [item.date, item.title]
68
+ }
69
+
70
+ state[:rss][r.url].reject! {|(date, title)|
71
+ p.items.none? {|item|
72
+ item.date == date && item.title == title
73
+ }
74
+ }
75
+ }
76
+ }
77
+
78
+ if digest
79
+ dispatch Herpes::Event.new {
80
+ events digest
81
+
82
+ def to_a
83
+ events
84
+ end
85
+ }
86
+ end
87
+ end
88
+ end
@@ -8,8 +8,8 @@
8
8
  # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
9
  #++
10
10
 
11
- module Herpes
11
+ class Herpes
12
12
  def self.version
13
- '0.0.1a1'
13
+ '0.0.1'
14
14
  end
15
15
  end
@@ -0,0 +1,26 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'actionpool'
12
+
13
+ class Workers
14
+ extend Forwardable
15
+
16
+ def_delegators :@pool, :max, :max=, :min, :min=
17
+
18
+ def initialize (range = 2 .. 4)
19
+ @pool = ActionPool::Pool.new(:min_threads => range.begin, :max_threads => range.end)
20
+ end
21
+
22
+ def do (*args, &block)
23
+ @pool.process(*args, &block)
24
+ end
25
+ end
26
+
data/lib/herpes.rb ADDED
@@ -0,0 +1,247 @@
1
+ #--
2
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3
+ # Version 2, December 2004
4
+ #
5
+ # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
6
+ # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
7
+ #
8
+ # 0. You just DO WHAT THE FUCK YOU WANT TO.
9
+ #++
10
+
11
+ require 'herpes/version'
12
+ require 'herpes/extensions'
13
+ require 'herpes/workers'
14
+ require 'herpes/event'
15
+ require 'herpes/module'
16
+
17
+ class Herpes
18
+ class Callback
19
+ attr_reader :time, :block, :last
20
+
21
+ def initialize (time, one_shot = false, &block)
22
+ raise ArgumentError, 'no block has been passed' unless block
23
+
24
+ @time = time
25
+ @one_shot = one_shot
26
+ @block = block
27
+
28
+ if !one_shot?
29
+ @last = Time.now - time
30
+ else
31
+ called!
32
+ end
33
+ end
34
+
35
+ def one_shot?
36
+ @one_shot
37
+ end
38
+
39
+ def next_in
40
+ time - (Time.now - last)
41
+ end
42
+
43
+ def gonna_call!
44
+ @calling = :gonna
45
+ end
46
+
47
+ def gonna_call?
48
+ @calling == :gonna
49
+ end
50
+
51
+ def calling!
52
+ @calling = :gonna
53
+ end
54
+
55
+ def called!
56
+ @last = Time.now
57
+ @calling = false
58
+ end
59
+
60
+ def calling?
61
+ @calling == true
62
+ end
63
+
64
+ def call (*args, &block)
65
+ return if calling?
66
+
67
+ calling!
68
+ @block.call(*args, &block)
69
+ called!
70
+ end
71
+ end
72
+
73
+ def self.load (*path)
74
+ new.load(*path)
75
+ end
76
+
77
+ attr_reader :workers, :modules
78
+
79
+ def initialize
80
+ @modules = []
81
+ @workers = Workers.new
82
+ @pipes = IO.pipe
83
+
84
+ @before = Hash.new { |h, k| h[k] = [] }
85
+ @matchers = Hash.new { |h, k| h[k] = [] }
86
+ @after = Hash.new { |h, k| h[k] = [] }
87
+
88
+ @callbacks = []
89
+ end
90
+
91
+ def state (path = nil)
92
+ if path && path != @path
93
+ @path = File.expand_path(path)
94
+ @state = Marshal.load(File.read(@path)) rescue nil
95
+ else
96
+ @state ||= {}
97
+ end
98
+ end
99
+
100
+ def save
101
+ return unless @state && @path
102
+
103
+ dump = Marshal.dump(@state)
104
+
105
+ File.open(@path, 'wb') { |f| f.write(dump) }
106
+ end
107
+
108
+ def load (*paths)
109
+ paths.each {|path|
110
+ instance_eval File.read(path), path, 1
111
+ }
112
+
113
+ self
114
+ end
115
+
116
+ def with (name, &block)
117
+ raise ArgumentError, "#{name} not found" unless Module[name]
118
+
119
+ Module[name].with(&block)
120
+ end
121
+
122
+ def use (name, &block)
123
+ raise ArgumentError, "#{name} not found" unless Module[name]
124
+
125
+ @modules << Module[name].use(self, &block)
126
+ end
127
+
128
+ def from (name, &block)
129
+ return unless block
130
+
131
+ @current, tmp = name, @current
132
+ result = instance_eval &block
133
+ @current = tmp
134
+ result
135
+ end
136
+
137
+ def before (&block)
138
+ @before[@current] = block
139
+ end
140
+
141
+ def on (matcher, &block)
142
+ return unless block
143
+
144
+ @matchers[@current] << Struct.new(:matcher, :block).new(matcher, block)
145
+ end
146
+
147
+ def after (&block)
148
+ @after[@current] = block
149
+ end
150
+
151
+ def dispatch (event = nil, &block)
152
+ if block && !event
153
+ event = Event.new(&block)
154
+ end
155
+
156
+ raise ArgumentError, 'you did not pass an Event' unless event.is_a?(Event)
157
+
158
+ @before.each {|name, block|
159
+ next unless name.nil? || (event.generated_by && event.generated_by =~ name)
160
+
161
+ block.call(event)
162
+ }
163
+
164
+ @matchers.each {|name, matchers|
165
+ next unless name.nil? || (event.generated_by && event.generated_by =~ name)
166
+
167
+ dispatched = false
168
+
169
+ matchers.each {|m|
170
+ begin
171
+ dispatched = true
172
+
173
+ m.block.call(event)
174
+ end if
175
+ (m.matcher == :anything) ||
176
+ (m.matcher == :anything_else && !dispatched) ||
177
+ (m.matcher.respond_to?(:call) && m.matcher.call(event))
178
+ }
179
+ }
180
+
181
+ @after.each {|name, block|
182
+ next unless name.nil? || (event.generated_by && event.generated_by =~ name)
183
+
184
+ block.call(event)
185
+ }
186
+ end
187
+
188
+ def every (time, &block)
189
+ @callbacks << Callback.new(time, &block)
190
+ wake_up
191
+ end
192
+
193
+ def after (time, &block)
194
+ @callbacks << Callback.new(time, true, &block)
195
+ wake_up
196
+ end
197
+
198
+ def until_next
199
+ next_in = @callbacks.min_by(&:next_in).next_in
200
+
201
+ next_in > 0 ? next_in : 0
202
+ rescue
203
+ 10
204
+ end
205
+
206
+ def sleep (time)
207
+ @pipes.first.read_nonblock 1337 rescue nil
208
+
209
+ IO.select([@pipes.first], nil, nil, time)
210
+ end
211
+
212
+ def wake_up
213
+ @pipes.last.write 'x'
214
+ end
215
+
216
+ def running?; !!@running; end
217
+ def stopped?; !@running; end
218
+
219
+ def start!
220
+ @running = true
221
+
222
+ while running?
223
+ sleep until_next
224
+
225
+ @callbacks.select {|callback|
226
+ callback.next_in <= 0
227
+ }.uniq.each {|callback|
228
+ @callbacks.delete(callback) if callback.one_shot?
229
+
230
+ next if callback.gonna_call?
231
+
232
+ callback.gonna_call!
233
+
234
+ workers.do {
235
+ callback.call
236
+ }
237
+ }
238
+ end
239
+
240
+ save
241
+ end
242
+
243
+ def stop!
244
+ @running = false;
245
+ wake_up
246
+ end
247
+ end
metadata CHANGED
@@ -1,26 +1,47 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: herpes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1a1
5
- prerelease: 5
4
+ version: 0.0.1
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - meh.
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-21 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2011-11-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: actionpool
16
+ requirement: &10434500 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *10434500
14
25
  description:
15
26
  email: meh@paranoici.org
16
- executables: []
27
+ executables:
28
+ - herpes
17
29
  extensions: []
18
30
  extra_rdoc_files: []
19
31
  files:
20
32
  - README.md
33
+ - bin/herpes
34
+ - examples/generator.rb
21
35
  - examples/rss.rb
22
36
  - herpes.gemspec
37
+ - lib/herpes.rb
38
+ - lib/herpes/email.rb
39
+ - lib/herpes/event.rb
40
+ - lib/herpes/extensions.rb
41
+ - lib/herpes/module.rb
42
+ - lib/herpes/rss.rb
23
43
  - lib/herpes/version.rb
44
+ - lib/herpes/workers.rb
24
45
  homepage: http://github.com/meh/herpes
25
46
  licenses: []
26
47
  post_install_message:
@@ -36,9 +57,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
36
57
  required_rubygems_version: !ruby/object:Gem::Requirement
37
58
  none: false
38
59
  requirements:
39
- - - ! '>'
60
+ - - ! '>='
40
61
  - !ruby/object:Gem::Version
41
- version: 1.3.1
62
+ version: '0'
42
63
  requirements: []
43
64
  rubyforge_project:
44
65
  rubygems_version: 1.8.10