gst-kitchen 0.2.0 → 0.4.0
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/README.md +4 -4
- data/bin/gst-kitchen +49 -2
- data/gst-kitchen.gemspec +2 -1
- data/lib/gst-kitchen.rb +1 -0
- data/lib/gst-kitchen/episode.rb +1 -3
- data/lib/gst-kitchen/feed.rb +71 -0
- data/lib/gst-kitchen/podcast.rb +9 -7
- data/lib/gst-kitchen/version.rb +1 -1
- data/templates/episodes.rss.erb +22 -22
- metadata +22 -5
data/README.md
CHANGED
|
@@ -35,7 +35,7 @@ gst-kitchen assumes various conventions about (file) naming and URLs.
|
|
|
35
35
|
|
|
36
36
|
Meta data from each episodes is read from your Auphonic production. The episode number is read from the `title`,
|
|
37
37
|
which must start with `<handle><number>`. The episode name is read from `subtitle`, summary and description is
|
|
38
|
-
set to Auphonic's `summary` field.
|
|
38
|
+
set to Auphonic's `summary` field. File sizes, playtime etc. are also read from the production.
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
## Usage
|
|
@@ -48,8 +48,8 @@ Then you need to create a `podcast.yml` containing your podcast metadata.
|
|
|
48
48
|
You can take a look at https://github.com/tisba/gst-website/blob/master/podcast.yml as an example.
|
|
49
49
|
Important fields are
|
|
50
50
|
|
|
51
|
-
* basic meta data like, `title`, `
|
|
52
|
-
* `handle` is a short *handle* for your podcast. It can be an abbreviation
|
|
51
|
+
* basic meta data like, `title`, `subtitle`, `author`, `email` and `language`
|
|
52
|
+
* `handle` is a short *handle* for your podcast. It can be an abbreviation, acronym, or anything you like. For geekstammtisch it's `GST`.
|
|
53
53
|
* base URLs for the website and media location
|
|
54
54
|
* list of available audio formats: fileext_encoding, e.g. `m4a_aac` or `mp3_mp3`
|
|
55
55
|
* `rss_output_path` specifies where the generated feeds will be located
|
|
@@ -84,4 +84,4 @@ Render all configured feeds based on all episodes in `episodes/`
|
|
|
84
84
|
5. Create new Pull Request
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
[igor]: https://github.com/m4p/fanboys-IGOR "IGOR"
|
|
87
|
+
[igor]: https://github.com/m4p/fanboys-IGOR "IGOR"
|
data/bin/gst-kitchen
CHANGED
|
@@ -9,7 +9,7 @@ require "fileutils"
|
|
|
9
9
|
|
|
10
10
|
SUB_COMMANDS = %w(feed process list version)
|
|
11
11
|
global_opts = Trollop::options do
|
|
12
|
-
banner "
|
|
12
|
+
banner "Geekstammtischs Kitchen to publish podcats..."
|
|
13
13
|
opt :podcast, "path of podcast YAML specification", :default => "podcast.yml"
|
|
14
14
|
stop_on SUB_COMMANDS
|
|
15
15
|
end
|
|
@@ -17,10 +17,12 @@ end
|
|
|
17
17
|
case ARGV.shift
|
|
18
18
|
when "version"
|
|
19
19
|
puts GstKitchen::VERSION
|
|
20
|
+
|
|
20
21
|
when "feed"
|
|
21
22
|
subcmd_opts = Trollop::options do
|
|
22
23
|
end
|
|
23
24
|
Podcast.from_yaml(global_opts[:podcast]).render_all_feeds
|
|
25
|
+
|
|
24
26
|
when "list"
|
|
25
27
|
productions = Auphonic::Account.init_from_system.productions
|
|
26
28
|
productions.each do |production|
|
|
@@ -33,13 +35,58 @@ when "process"
|
|
|
33
35
|
end
|
|
34
36
|
production = Auphonic::Account.init_from_system.production(subcmd_opts[:uuid])
|
|
35
37
|
|
|
36
|
-
podcast = Podcast.from_yaml(
|
|
38
|
+
podcast = Podcast.from_yaml(global_opts[:podcast])
|
|
37
39
|
FileUtils.mkdir_p podcast.episodes_path
|
|
38
40
|
episode = podcast.create_episode_from_auphonic production
|
|
39
41
|
|
|
40
42
|
puts "Episode: #{episode}"
|
|
41
43
|
puts "Writing episode YAML to #{File.join(podcast.episodes_path, episode.handle.downcase)}.yml"
|
|
42
44
|
podcast.export_episode(episode)
|
|
45
|
+
|
|
46
|
+
when "stats"
|
|
47
|
+
podcast = Podcast.from_yaml(global_opts[:podcast])
|
|
48
|
+
if STDIN.tty?
|
|
49
|
+
STDERR.puts "You need to pipe logs to gst-kitchen stats, e.g. cat logs/* | gst-kitchen stats"
|
|
50
|
+
exit
|
|
51
|
+
end
|
|
52
|
+
downloads = {}
|
|
53
|
+
while (logline = STDIN.gets)
|
|
54
|
+
# 93.194.154.91 - - [13/Dec/2012:16:22:38 +0100] "GET /episodes/gst000.m4a HTTP/1.1" 200 18676513 "-" "iTunes/11.0 (Macintosh; OS X 10.8.2) AppleWebKit/536.26.14"
|
|
55
|
+
|
|
56
|
+
/ - - \[(?<date>.*?)\].*?(?<filename>gst\d{3}\....).*?2\d\d (?<size>\d+) / =~ logline
|
|
57
|
+
|
|
58
|
+
next unless filename
|
|
59
|
+
|
|
60
|
+
handle, format = filename.split(".")
|
|
61
|
+
|
|
62
|
+
next unless episode = podcast.episode_by_handle(handle)
|
|
63
|
+
next unless ["mp3", "m4a"].include? format
|
|
64
|
+
|
|
65
|
+
downloads[handle] ||= {}
|
|
66
|
+
downloads[handle][format] ||= {}
|
|
67
|
+
|
|
68
|
+
media_format = format == "mp3" ? "mp3" : "aac"
|
|
69
|
+
|
|
70
|
+
downloads[handle][format][:size] ||= episode.media[media_format]["size"]
|
|
71
|
+
downloads[handle][format][:downloaded] ||= 0
|
|
72
|
+
downloads[handle][format][:downloaded] += size.to_i
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
total_downloads = {}
|
|
76
|
+
downloads.each do |handle, episode|
|
|
77
|
+
episode.each do |format, stats|
|
|
78
|
+
downloads[handle][format][:count] = stats[:downloaded].to_f / stats[:size]
|
|
79
|
+
total_downloads[handle] ||= {}
|
|
80
|
+
total_downloads[handle]["total_downloads"] ||= 0
|
|
81
|
+
total_downloads[handle]["total_downloads"] += downloads[handle][format][:count]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
downloads.keys.each do |handle|
|
|
86
|
+
downloads[handle].merge!(total_downloads[handle])
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
puts downloads.to_yaml
|
|
43
90
|
else
|
|
44
91
|
Trollop::die "unknown subcommand #{subcmd.inspect}"
|
|
45
92
|
end
|
data/gst-kitchen.gemspec
CHANGED
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
|
|
|
10
10
|
gem.email = ["sebastian.cohnen@gmx.net"]
|
|
11
11
|
gem.description = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
|
|
12
12
|
gem.summary = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
|
|
13
|
-
gem.homepage = ""
|
|
13
|
+
gem.homepage = "http://github.com/tisba/gst-kitchen"
|
|
14
14
|
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
|
|
|
20
20
|
gem.add_dependency "yajl-ruby"
|
|
21
21
|
gem.add_dependency "trollop"
|
|
22
22
|
gem.add_dependency "rake"
|
|
23
|
+
gem.add_dependency "redcarpet"
|
|
23
24
|
end
|
data/lib/gst-kitchen.rb
CHANGED
data/lib/gst-kitchen/episode.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :published_at, :summary
|
|
1
|
+
class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :published_at, :summary)
|
|
2
2
|
include Comparable
|
|
3
3
|
|
|
4
4
|
def self.from_auphonic(production)
|
|
@@ -11,7 +11,6 @@ class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :pub
|
|
|
11
11
|
length: data["data"]["length"],
|
|
12
12
|
name: data["data"]["metadata"]["subtitle"].strip,
|
|
13
13
|
summary: data["data"]["metadata"]["summary"].strip,
|
|
14
|
-
description: data["data"]["metadata"]["summary"].strip
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
media = data["data"]["output_files"].each_with_object({}) do |item, obj|
|
|
@@ -29,7 +28,6 @@ class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :pub
|
|
|
29
28
|
episode.auphonic_uuid = metadata[:auphonic_uuid]
|
|
30
29
|
episode.published_at = Time.now
|
|
31
30
|
episode.summary = metadata[:summary]
|
|
32
|
-
episode.description = metadata[:description]
|
|
33
31
|
episode.media = media
|
|
34
32
|
|
|
35
33
|
episode
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'redcarpet'
|
|
2
|
+
require 'ruby-debug'
|
|
3
|
+
|
|
4
|
+
class GstKitchen::Feed
|
|
5
|
+
|
|
6
|
+
class ShownotesRenderer < Redcarpet::Render::HTML
|
|
7
|
+
|
|
8
|
+
TWITTER_HANDLE_PATTER = /(\W)@(\w+)\b/
|
|
9
|
+
|
|
10
|
+
def preprocess(full_document)
|
|
11
|
+
return full_document if full_document.nil?
|
|
12
|
+
|
|
13
|
+
# Duplicate the string so we don't alter the original, then call to_str
|
|
14
|
+
# to cast it back to a String instead of a SafeBuffer. This is required
|
|
15
|
+
# for gsub calls to work as we need them to.
|
|
16
|
+
full_document = full_document.dup.to_str
|
|
17
|
+
|
|
18
|
+
# Extract pre blocks so they are not altered
|
|
19
|
+
# from http://github.github.com/github-flavored-markdown/
|
|
20
|
+
extractions = {}
|
|
21
|
+
full_document.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
|
|
22
|
+
md5 = Digest::MD5.hexdigest(match)
|
|
23
|
+
extractions[md5] = match
|
|
24
|
+
"{gfm-extraction-#{md5}}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
full_document = parse(full_document)
|
|
28
|
+
|
|
29
|
+
# Insert pre block extractions
|
|
30
|
+
full_document.gsub!(/\{gfm-extraction-(\h{32})\}/) do
|
|
31
|
+
extractions[$1]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return full_document
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def parse(document)
|
|
40
|
+
parse_twitter_handle(document)
|
|
41
|
+
|
|
42
|
+
return document
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse_twitter_handle(document)
|
|
46
|
+
document.gsub!(TWITTER_HANDLE_PATTER, '\1[@\2](https://twitter.com/\2)')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
TEMPLATE_PATH = File.join(File.dirname(__FILE__), "..", "..", "templates")
|
|
52
|
+
|
|
53
|
+
attr_reader :format, :template
|
|
54
|
+
|
|
55
|
+
def initialize(options = {})
|
|
56
|
+
@format = options[:format]
|
|
57
|
+
@template = ERB.new(File.read(File.join(TEMPLATE_PATH, "#{options[:template]}.rss.erb")))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def render_as_markdown(text)
|
|
61
|
+
markdown = Redcarpet::Markdown.new(ShownotesRenderer, autolink: true)
|
|
62
|
+
markdown.render text
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def to_xml(variables = {})
|
|
66
|
+
variables.each { |var, value| instance_variable_set("@#{var}", value) }
|
|
67
|
+
|
|
68
|
+
@template.result(binding)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
data/lib/gst-kitchen/podcast.rb
CHANGED
|
@@ -50,6 +50,12 @@ class Podcast
|
|
|
50
50
|
Episode.from_auphonic production
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def episode_by_handle(handle)
|
|
54
|
+
self.episodes.find do |episode|
|
|
55
|
+
episode.handle.downcase == handle.downcase
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
53
59
|
def feed_url(format)
|
|
54
60
|
url = URI(self.website)
|
|
55
61
|
url.path = "/#{rss_file(format)}"
|
|
@@ -78,15 +84,10 @@ class Podcast
|
|
|
78
84
|
self
|
|
79
85
|
end
|
|
80
86
|
|
|
81
|
-
def current_format
|
|
82
|
-
@current_format
|
|
83
|
-
end
|
|
84
|
-
|
|
85
87
|
def render_feed(format)
|
|
86
|
-
|
|
87
|
-
template = ERB.new File.read(File.join(File.dirname(__FILE__), "../..", "templates/episodes.rss.erb"))
|
|
88
|
+
feed = GstKitchen::Feed.new format: format, template: "episodes"
|
|
88
89
|
File.open(File.join(rss_output_path, rss_file(format)), "w") do |rss|
|
|
89
|
-
rss.write
|
|
90
|
+
rss.write feed.to_xml(podcast: podcast)
|
|
90
91
|
end
|
|
91
92
|
end
|
|
92
93
|
|
|
@@ -106,6 +107,7 @@ class Podcast
|
|
|
106
107
|
end
|
|
107
108
|
|
|
108
109
|
private
|
|
110
|
+
|
|
109
111
|
def rss_file(format)
|
|
110
112
|
"episodes.#{format.file_ext}.rss"
|
|
111
113
|
end
|
data/lib/gst-kitchen/version.rb
CHANGED
data/templates/episodes.rss.erb
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
2
|
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
|
|
3
3
|
<channel>
|
|
4
|
-
<title><%= podcast.title %></title>
|
|
5
|
-
<link><%= podcast.website %></link>
|
|
6
|
-
<atom:link rel="self" href="<%= podcast.feed_url(
|
|
7
|
-
<% podcast.other_formats(
|
|
8
|
-
<atom:link rel="alternate" href="<%= podcast.feed_url(format) %>" type="application/rss+xml" title="<%= format.file_ext.upcase %> Audio"/>
|
|
4
|
+
<title><%= @podcast.title %></title>
|
|
5
|
+
<link><%= @podcast.website %></link>
|
|
6
|
+
<atom:link rel="self" href="<%= @podcast.feed_url(@format) %>" type="application/rss+xml" title="<%= @format.file_ext.upcase %> Audio"/>
|
|
7
|
+
<% @podcast.other_formats(@format).each do |format| %>
|
|
8
|
+
<atom:link rel="alternate" href="<%= @podcast.feed_url(format) %>" type="application/rss+xml" title="<%= format.file_ext.upcase %> Audio"/>
|
|
9
9
|
<% end %>
|
|
10
|
-
<language><%= podcast.language %></language>
|
|
10
|
+
<language><%= @podcast.language %></language>
|
|
11
11
|
<generator>gst-kitchen</generator>
|
|
12
|
-
<copyright><%= podcast.author %>, cc-by-nc-sa</copyright>
|
|
13
|
-
<itunes:subtitle><%= podcast.subtitle %></itunes:subtitle>
|
|
14
|
-
<itunes:author><%= podcast.author %></itunes:author>
|
|
15
|
-
<managingEditor><%= podcast.email %> (<%= podcast.author %>)</managingEditor>
|
|
16
|
-
<itunes:summary><%= podcast.summary %></itunes:summary>
|
|
17
|
-
<itunes:explicit><%= podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
18
|
-
<description><%= podcast.summary %></description>
|
|
12
|
+
<copyright><%= @podcast.author %>, cc-by-nc-sa</copyright>
|
|
13
|
+
<itunes:subtitle><%= @podcast.subtitle %></itunes:subtitle>
|
|
14
|
+
<itunes:author><%= @podcast.author %></itunes:author>
|
|
15
|
+
<managingEditor><%= @podcast.email %> (<%= @podcast.author %>)</managingEditor>
|
|
16
|
+
<itunes:summary><%= @podcast.summary %></itunes:summary>
|
|
17
|
+
<itunes:explicit><%= @podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
18
|
+
<description><%= @podcast.summary %></description>
|
|
19
19
|
<itunes:owner>
|
|
20
|
-
<itunes:name><%= podcast.author %></itunes:name>
|
|
21
|
-
<itunes:email><%= podcast.email %></itunes:email>
|
|
20
|
+
<itunes:name><%= @podcast.author %></itunes:name>
|
|
21
|
+
<itunes:email><%= @podcast.email %></itunes:email>
|
|
22
22
|
</itunes:owner>
|
|
23
23
|
|
|
24
|
-
<itunes:image href="<%= podcast.cover_url %>"/>
|
|
24
|
+
<itunes:image href="<%= @podcast.cover_url %>"/>
|
|
25
25
|
|
|
26
26
|
<itunes:category text="Technology"/>
|
|
27
27
|
<itunes:category text="Technology">
|
|
@@ -31,19 +31,19 @@
|
|
|
31
31
|
<itunes:category text="Tech News"/>
|
|
32
32
|
</itunes:category>
|
|
33
33
|
|
|
34
|
-
<% podcast.episodes.each do |episode| %>
|
|
34
|
+
<% @podcast.episodes.each do |episode| %>
|
|
35
35
|
<item>
|
|
36
36
|
<title><%= episode.title %></title>
|
|
37
|
-
<itunes:explicit><%= podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
38
|
-
<itunes:author><%= podcast.author %></itunes:author>
|
|
37
|
+
<itunes:explicit><%= @podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
38
|
+
<itunes:author><%= @podcast.author %></itunes:author>
|
|
39
39
|
<itunes:subtitle><%= episode.title %></itunes:subtitle>
|
|
40
|
-
<enclosure url="<%= podcast.episode_media_url(episode,
|
|
40
|
+
<enclosure url="<%= @podcast.episode_media_url(episode, @format) %>" length="<%= episode.media[@format.format]["size"] %>" type="<%= @format.mime_type %>"/>
|
|
41
41
|
<guid isPermaLink="false"><%= episode.handle %></guid>
|
|
42
42
|
<pubDate><%= episode.rfc_2822_date %></pubDate>
|
|
43
43
|
<itunes:duration><%= episode.duration %></itunes:duration>
|
|
44
44
|
<itunes:summary><![CDATA[<%= episode.summary %>]]></itunes:summary>
|
|
45
|
-
<description><![CDATA[<%= episode.
|
|
46
|
-
<atom:link rel="http://podlove.org/deep-link" href="<%= podcast.deep_link_url(episode) %>" />
|
|
45
|
+
<description><![CDATA[<%= render_as_markdown episode.summary %>]]></description>
|
|
46
|
+
<atom:link rel="http://podlove.org/deep-link" href="<%= @podcast.deep_link_url(episode) %>" />
|
|
47
47
|
</item>
|
|
48
48
|
<% end %>
|
|
49
49
|
</channel>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gst-kitchen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2013-01-28 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: yajl-ruby
|
|
@@ -59,6 +59,22 @@ dependencies:
|
|
|
59
59
|
- - ! '>='
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
61
|
version: '0'
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: redcarpet
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
type: :runtime
|
|
71
|
+
prerelease: false
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
62
78
|
description: gst-kitchen is a gem to publish podcasts like a nerd with auphonic!
|
|
63
79
|
email:
|
|
64
80
|
- sebastian.cohnen@gmx.net
|
|
@@ -79,11 +95,12 @@ files:
|
|
|
79
95
|
- lib/gst-kitchen/auphonic/account.rb
|
|
80
96
|
- lib/gst-kitchen/auphonic/production.rb
|
|
81
97
|
- lib/gst-kitchen/episode.rb
|
|
98
|
+
- lib/gst-kitchen/feed.rb
|
|
82
99
|
- lib/gst-kitchen/media.rb
|
|
83
100
|
- lib/gst-kitchen/podcast.rb
|
|
84
101
|
- lib/gst-kitchen/version.rb
|
|
85
102
|
- templates/episodes.rss.erb
|
|
86
|
-
homepage:
|
|
103
|
+
homepage: http://github.com/tisba/gst-kitchen
|
|
87
104
|
licenses: []
|
|
88
105
|
post_install_message:
|
|
89
106
|
rdoc_options: []
|
|
@@ -97,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
97
114
|
version: '0'
|
|
98
115
|
segments:
|
|
99
116
|
- 0
|
|
100
|
-
hash:
|
|
117
|
+
hash: 1700055491858221400
|
|
101
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
119
|
none: false
|
|
103
120
|
requirements:
|
|
@@ -106,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
106
123
|
version: '0'
|
|
107
124
|
segments:
|
|
108
125
|
- 0
|
|
109
|
-
hash:
|
|
126
|
+
hash: 1700055491858221400
|
|
110
127
|
requirements: []
|
|
111
128
|
rubyforge_project:
|
|
112
129
|
rubygems_version: 1.8.24
|