herpes 0.0.1a1 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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