concerto_simple_rss 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7621d623a546cb35b8ac6671dbdba81f570ebfb
4
- data.tar.gz: 6a1ac67eb1add40753f8eb0d73f5c43e9d7badbc
3
+ metadata.gz: aa7e279123911a7477853d01e1ac97f6a9a9d6b9
4
+ data.tar.gz: 9f34f22652616db1752b96f52431aef37809d6c0
5
5
  SHA512:
6
- metadata.gz: 8d139ccbfe054efd6cc6ad626958354bd2de718e1651cc091f8ef24e30cb4a45257520e94ba8c8c02633b42978fcae2bb4e9f3bd3e1e9158ba86fa0d3f701a58
7
- data.tar.gz: 475cba55dc73ca805b7511242bc08d8b9dc5d7e009f7d233eb31a9e91e4db18a0426343e468497f89438fdbebcd1a982febe6450e8bea2496e0100b6b3ceaef6
6
+ metadata.gz: d3052c45ead7cabeb914416882979d34fcec3f207ad5d906acc6aaecf08d28101c7d4a92b43dbd67346032dac7b083c8ad50d3867ee9c6dc10fa8ae687804254
7
+ data.tar.gz: 6a341b98a8a0471de16426be656a717b9d0306f080710581adc41c8720080c8acecbc44c25eb23e4189814e619cb0ccf3fbb1f2e4ba0e22f4db023e8809f3409
@@ -12,6 +12,8 @@ function previewSimpleRss() {
12
12
  var url = $('input#simple_rss_config_url').data('url');
13
13
  if (url) {
14
14
  rss_url = $('input#simple_rss_config_url').val();
15
+ userid = $('input#simple_rss_config_url_userid').val();
16
+ password = $('input#simple_rss_config_url_password').val();
15
17
  output_format = $('select#simple_rss_config_output_format').val();
16
18
  max_items = $('input#simple_rss_config_max_items').val();
17
19
  reverse_order = $('select#simple_rss_config_reverse_order').val();
@@ -22,6 +24,8 @@ function previewSimpleRss() {
22
24
  }
23
25
  $("#preview_div").load(url, { data: {
24
26
  url: rss_url,
27
+ url_userid: userid,
28
+ url_password: password,
25
29
  output_format: output_format,
26
30
  max_items: max_items,
27
31
  reverse_order: reverse_order,
@@ -40,6 +44,8 @@ function initializeSimpleRssHandlers() {
40
44
  // on blur and change of url, display format, reverse_order, max items, and xsl
41
45
  // not on keyup (poor man's debouncing technique?)
42
46
  $('input#simple_rss_config_url').on('blur', previewSimpleRss);
47
+ $('input#simple_rss_config_url_userid').on('blur', previewSimpleRss);
48
+ $('input#simple_rss_config_url_password').on('blur', previewSimpleRss);
43
49
  $('select#simple_rss_config_output_format').on('change', previewSimpleRss);
44
50
  $('input#simple_rss_config_max_items').on('blur', previewSimpleRss);
45
51
  $('select#simple_rss_config_reverse_order').on('change', previewSimpleRss);
@@ -1,137 +1,176 @@
1
1
  class SimpleRss < DynamicContent
2
+ require 'base64'
2
3
 
3
4
  DISPLAY_NAME = 'RSS Feed'
4
5
 
5
6
  validate :validate_config, :validate_feed
6
7
 
8
+ # Load a configuration hash.
9
+ # Converts the JSON data stored for the content into the configuration.
10
+ # Called during `after_find`.
11
+ def load_config
12
+ j = JSON.load(self.data)
13
+
14
+ # decrypt fields
15
+ unless j.blank?
16
+ encrypted_userid = Base64.decode64(j['url_userid_enc']) unless j['url_userid_enc'].blank?
17
+ encrypted_password = Base64.decode64(j['url_password_enc']) unless j['url_password_enc'].blank?
18
+
19
+ j['url_userid'] = (encrypted_userid.blank? ? "" : encrypted_userid.decrypt)
20
+ j['url_password'] = (encrypted_password.blank? ? "" : encrypted_password.decrypt)
21
+ end
22
+
23
+ self.config = j
24
+ end
25
+
26
+ # Prepare the configuration to be saved.
27
+ # Compress the config hash back into JSON to be stored in the database.
28
+ # Called during `before_validation`.
29
+ def save_config
30
+ j = self.config.deep_dup
31
+
32
+ # encrypt fields
33
+ j['url_userid_enc'] = (j['url_userid'].blank? ? "" : Base64.encode64(j['url_userid'].encrypt))
34
+ j['url_password_enc'] = (j['url_password'].blank? ? "" : Base64.encode64(j['url_password'].encrypt))
35
+ j.delete 'url_userid'
36
+ j.delete 'url_password'
37
+ self.data = JSON.dump(j)
38
+ end
39
+
7
40
  def build_content
8
41
  contents = []
9
42
 
10
43
  url = self.config['url']
11
- type, feed_title, rss, raw = fetch_feed(url)
12
-
13
- if (["RSS", "ATOM"].include? type) && !feed_title.blank?
14
- # it is a valid feed
15
- if !self.config['reverse_order'].blank? && self.config['reverse_order'] == '1'
16
- rss.items.reverse!
17
- end
18
- feed_items = rss.items
19
- if !self.config['max_items'].blank? && self.config['max_items'].to_i > 0
20
- feed_items = feed_items.first(self.config['max_items'].to_i)
21
- end
22
- case self.config['output_format']
23
- when 'headlines'
24
- feed_items.each_slice(5).with_index do |items, index|
25
- htmltext = HtmlText.new()
26
- htmltext.name = "#{feed_title} (#{index+1})"
27
- htmltext.data = sanitize("<h1>#{feed_title}</h1> #{items_to_html(items, type)}")
28
- contents << htmltext
44
+ unless url.blank?
45
+ url_userid = self.config['url_userid']
46
+ url_password = self.config['url_password']
47
+ type, feed_title, rss, raw = fetch_feed(url, url_userid, url_password)
48
+
49
+ if (["RSS", "ATOM"].include? type) && !feed_title.blank?
50
+ # it is a valid feed
51
+ if !self.config['reverse_order'].blank? && self.config['reverse_order'] == '1'
52
+ rss.items.reverse!
29
53
  end
30
- when 'detailed'
31
- feed_items.each_with_index do |item, index|
32
- htmltext = HtmlText.new()
33
- htmltext.name = "#{feed_title} (#{index+1})"
34
- htmltext.data = sanitize(item_to_html(item, type))
35
- contents << htmltext
36
- end
37
- when 'xslt'
38
- require 'rexml/document'
39
- require 'xml/xslt'
40
-
41
- #XML::XSLT.registerErrorHandler { |string| puts string }
42
- xslt = XML::XSLT.new()
43
- begin
44
- xslt.xml = REXML::Document.new(raw)
45
- rescue REXML::ParseException => e
46
- Rails.logger.error("Unable to parse incoming feed: #{e.message}")
47
- raise "Unable to parse incoming feed. "
48
- rescue => e
49
- raise e
54
+ feed_items = rss.items
55
+ if !self.config['max_items'].blank? && self.config['max_items'].to_i > 0
56
+ feed_items = feed_items.first(self.config['max_items'].to_i)
50
57
  end
58
+ case self.config['output_format']
59
+ when 'headlines'
60
+ feed_items.each_slice(5).with_index do |items, index|
61
+ htmltext = HtmlText.new()
62
+ htmltext.name = "#{feed_title} (#{index+1})"
63
+ htmltext.data = sanitize("<h1>#{feed_title}</h1> #{items_to_html(items, type)}")
64
+ contents << htmltext
65
+ end
66
+ when 'detailed'
67
+ feed_items.each_with_index do |item, index|
68
+ htmltext = HtmlText.new()
69
+ htmltext.name = "#{feed_title} (#{index+1})"
70
+ htmltext.data = sanitize(item_to_html(item, type))
71
+ contents << htmltext
72
+ end
73
+ when 'xslt'
74
+ require 'rexml/document'
75
+ require 'xml/xslt'
51
76
 
52
- begin
53
- xslt.xsl = REXML::Document.new(self.config['xsl'])
54
- rescue REXML::ParseException => e
55
- Rails.logger.error("Unable to parse Xsl: #{e.message}")
56
- # fmt is <rexml::parseexception: message :> trace ... so just pull out the message
57
- s = e.message
58
- msg_stop = s.index(">")
59
- s = s.slice(23, msg_stop - 23) if !msg_stop.nil?
60
- raise "Unable to parse Xsl. #{s}"
61
- rescue => e
62
- raise e
63
- end
77
+ #XML::XSLT.registerErrorHandler { |string| puts string }
78
+ xslt = XML::XSLT.new()
79
+ begin
80
+ xslt.xml = REXML::Document.new(raw)
81
+ rescue REXML::ParseException => e
82
+ Rails.logger.error("Unable to parse incoming feed: #{e.message}")
83
+ raise "Unable to parse incoming feed. "
84
+ rescue => e
85
+ raise e
86
+ end
64
87
 
65
- # add a replace [gsub] function for more powerful transforms. You can use this in a transform
66
- # by adding the bogus namespace http://concerto.functions
67
- # A nodeset comes in as an array of REXML::Elements
68
- XML::XSLT.registerExtFunc("http://concerto.functions", "replace") do |nodes, pattern, replacement|
69
- result = xslt_replace(nodes, pattern, replacement)
70
- result
71
- end
88
+ begin
89
+ xslt.xsl = REXML::Document.new(self.config['xsl'])
90
+ rescue REXML::ParseException => e
91
+ Rails.logger.error("Unable to parse Xsl: #{e.message}")
92
+ # fmt is <rexml::parseexception: message :> trace ... so just pull out the message
93
+ s = e.message
94
+ msg_stop = s.index(">")
95
+ s = s.slice(23, msg_stop - 23) if !msg_stop.nil?
96
+ raise "Unable to parse Xsl. #{s}"
97
+ rescue => e
98
+ raise e
99
+ end
72
100
 
73
- XML::XSLT.registerExtFunc("http://schemas.concerto-signage.org/functions", "replace") do |nodes, pattern, replacement|
74
- result = xslt_replace(nodes, pattern, replacement)
75
- result
76
- end
101
+ # add a replace [gsub] function for more powerful transforms. You can use this in a transform
102
+ # by adding the bogus namespace http://concerto.functions
103
+ # A nodeset comes in as an array of REXML::Elements
104
+ XML::XSLT.registerExtFunc("http://concerto.functions", "replace") do |nodes, pattern, replacement|
105
+ result = xslt_replace(nodes, pattern, replacement)
106
+ result
107
+ end
77
108
 
78
- data = xslt.serve()
79
- # xslt.serve does always return a string with ASCII-8BIT encoding regardless of what the actual encoding is
80
- data = data.force_encoding(xslt.xml.encoding) if data
81
-
82
- # try to load the transformed data as an xml document so we can see if there are
83
- # mulitple content-items that we need to parse out, if we cant then treat it as one content item
84
- begin
85
- data_xml = REXML::Document.new('<root>' + data + '</root>')
86
- nodes = REXML::XPath.match(data_xml, "//content-item")
87
- # if there are no content-items then add the whole result (data) as one content
88
- if nodes.count == 0
89
- htmltext = HtmlText.new()
90
- htmltext.name = "#{feed_title}"
91
- htmltext.data = sanitize(data)
92
- contents << htmltext
93
- else
94
- # if there are any content-items then add each one as a separate content
95
- # and strip off the content-item wrapper
96
- nodes.each do |n|
109
+ XML::XSLT.registerExtFunc("http://schemas.concerto-signage.org/functions", "replace") do |nodes, pattern, replacement|
110
+ result = xslt_replace(nodes, pattern, replacement)
111
+ result
112
+ end
113
+
114
+ data = xslt.serve()
115
+ # xslt.serve does always return a string with ASCII-8BIT encoding regardless of what the actual encoding is
116
+ data = data.force_encoding(xslt.xml.encoding) if data
117
+
118
+ # try to load the transformed data as an xml document so we can see if there are
119
+ # mulitple content-items that we need to parse out, if we cant then treat it as one content item
120
+ begin
121
+ data_xml = REXML::Document.new('<root>' + data + '</root>')
122
+ nodes = REXML::XPath.match(data_xml, "//content-item")
123
+ # if there are no content-items then add the whole result (data) as one content
124
+ if nodes.count == 0
97
125
  htmltext = HtmlText.new()
98
126
  htmltext.name = "#{feed_title}"
99
- htmltext.data = sanitize(n.to_s.gsub(/^\s*\<content-item\>/, '').gsub(/\<\/content-item\>\s*$/,''))
127
+ htmltext.data = sanitize(data)
100
128
  contents << htmltext
129
+ else
130
+ # if there are any content-items then add each one as a separate content
131
+ # and strip off the content-item wrapper
132
+ nodes.each do |n|
133
+ htmltext = HtmlText.new()
134
+ htmltext.name = "#{feed_title}"
135
+ htmltext.data = sanitize(n.to_s.gsub(/^\s*\<content-item\>/, '').gsub(/\<\/content-item\>\s*$/,''))
136
+ contents << htmltext
137
+ end
101
138
  end
102
- end
103
- rescue => e
104
- # maybe the html was not xml compliant-- this happens frequently in rss feed descriptions
105
- # look for another separator and use it, if it exists
106
-
107
- if data.include?("</content-item>")
108
- # if there are any content-items then add each one as a separate content
109
- # and strip off the content-item wrapper
110
- data.split("</content-item>").each do |n|
139
+ rescue => e
140
+ # maybe the html was not xml compliant-- this happens frequently in rss feed descriptions
141
+ # look for another separator and use it, if it exists
142
+
143
+ if data.include?("</content-item>")
144
+ # if there are any content-items then add each one as a separate content
145
+ # and strip off the content-item wrapper
146
+ data.split("</content-item>").each do |n|
147
+ htmltext = HtmlText.new()
148
+ htmltext.name = "#{feed_title}"
149
+ htmltext.data = sanitize(n.sub("<content-item>", ""))
150
+ contents << htmltext if !htmltext.data.strip.blank?
151
+ end
152
+
153
+ else
154
+ Rails.logger.error("unable to parse resultant xml, assuming it is one content item #{e.message}")
155
+ # raise "unable to parse resultant xml #{e.message}"
156
+ # add the whole result as one content
111
157
  htmltext = HtmlText.new()
112
158
  htmltext.name = "#{feed_title}"
113
- htmltext.data = sanitize(n.sub("<content-item>", ""))
114
- contents << htmltext if !htmltext.data.strip.blank?
159
+ htmltext.data = sanitize(data)
160
+ contents << htmltext
115
161
  end
116
-
117
- else
118
- Rails.logger.error("unable to parse resultant xml, assuming it is one content item #{e.message}")
119
- # raise "unable to parse resultant xml #{e.message}"
120
- # add the whole result as one content
121
- htmltext = HtmlText.new()
122
- htmltext.name = "#{feed_title}"
123
- htmltext.data = sanitize(data)
124
- contents << htmltext
125
162
  end
163
+ else
164
+ raise ArgumentError, 'Unexpected output format for RSS feed.'
126
165
  end
166
+ elsif type == "ERROR"
167
+ raise rss
127
168
  else
128
- raise ArgumentError, 'Unexpected output format for RSS feed.'
169
+ Rails.logger.error("could not fetch #{type} feed for #{feed_title} at #{url}")
170
+ raise "Unexpected feed format for #{url}."
129
171
  end
130
- else
131
- Rails.logger.error("could not fetch #{type} feed for #{feed_title} at #{url}")
132
- raise "Unexpected feed format for #{url}."
133
172
  end
134
-
173
+
135
174
  return contents
136
175
  end
137
176
 
@@ -162,7 +201,8 @@ class SimpleRss < DynamicContent
162
201
  end
163
202
 
164
203
  # fetch the feed, return the type, title, and contents (parsed) and raw feed (unparsed)
165
- def fetch_feed(url)
204
+ def fetch_feed(url, url_userid, url_password)
205
+ require 'encryptor'
166
206
  require 'rss'
167
207
  require 'open-uri'
168
208
 
@@ -171,28 +211,35 @@ class SimpleRss < DynamicContent
171
211
  rss = nil
172
212
  feed = nil
173
213
 
174
- begin
175
- # cache same url for 1 minute to alleviate redundant calls when previewing
176
- feed = Rails.cache.fetch(url, :expires_in => 1.minute) do
177
- open(url).read()
178
- end
179
-
180
- rss = RSS::Parser.parse(feed, false, true)
181
- raise "feed could not be parsed" if rss.nil?
182
- rescue => e
183
- # cant parse rss or url is bad
184
- Rails.logger.debug("unable to fetch or parse feed - #{url}, #{e.message}")
185
- rss = e.message
186
- else
187
- type = rss.feed_type.upcase
214
+ unless url.blank?
215
+ begin
216
+ # cache same url for 1 minute to alleviate redundant calls when previewing
217
+ feed = Rails.cache.fetch(url, :expires_in => 1.minute) do
218
+ if url_userid.blank? or url_password.blank?
219
+ open(url).read()
220
+ else
221
+ open(url, http_basic_authentication: [url_userid, url_password]).read()
222
+ end
223
+ end
188
224
 
189
- case type
190
- when "RSS"
191
- title = rss.channel.title
192
- when "ATOM"
193
- title = rss.title.content
225
+ rss = RSS::Parser.parse(feed, false, true)
226
+ raise "feed could not be parsed" if rss.nil?
227
+ rescue => e
228
+ # cant parse rss or url is bad
229
+ Rails.logger.debug("unable to fetch or parse feed - #{url}, #{e.message}")
230
+ rss = e.message
231
+ type = "ERROR"
194
232
  else
195
- #title = "unknown feed type"
233
+ type = rss.feed_type.upcase
234
+
235
+ case type
236
+ when "RSS"
237
+ title = rss.channel.title
238
+ when "ATOM"
239
+ title = rss.title.content
240
+ else
241
+ #title = "unknown feed type"
242
+ end
196
243
  end
197
244
  end
198
245
 
@@ -238,16 +285,18 @@ class SimpleRss < DynamicContent
238
285
  # Simple RSS processing needs a feed URL and the format of the output content.
239
286
  def self.form_attributes
240
287
  attributes = super()
241
- attributes.concat([:config => [:url, :output_format, :reverse_order, :max_items, :xsl, :sanitize_tags]])
288
+ attributes.concat([:config => [:url, :url_userid, :url_password, :output_format, :reverse_order, :max_items, :xsl, :sanitize_tags]])
242
289
  end
243
290
 
244
291
  # if the feed is valid we store the title in config
245
292
  def validate_feed
246
293
  url = self.config['url']
294
+ url_userid = self.config['url_userid']
295
+ url_password = self.config['url_password']
247
296
  unless url.blank?
248
- Rails.logger.debug("looking up feed title for #{url}")
297
+ Rails.logger.debug("looking up feed title for #{url}")
249
298
 
250
- type, title = fetch_feed(url)
299
+ type, title = fetch_feed(url, url_userid, url_password)
251
300
  if (["RSS", "ATOM"].include? type) && !title.blank?
252
301
  self.config['title'] = title
253
302
  else
@@ -270,11 +319,13 @@ class SimpleRss < DynamicContent
270
319
  errors.add(:base, "XSL Markup can't be blank when using the XSLT Display Format")
271
320
  else
272
321
  url = self.config['url']
322
+ url_userid = self.config['url_userid']
323
+ url_password = self.config['url_password']
273
324
  unless url.blank?
274
325
  require 'rexml/document'
275
326
  require 'xml/xslt'
276
327
 
277
- type, title, rss, raw = fetch_feed(url)
328
+ type, title, rss, raw = fetch_feed(url, url_userid, url_password)
278
329
  if ["RSS", "ATOM"].include? type
279
330
  begin
280
331
  xslt = XML::XSLT.new()
@@ -295,6 +346,8 @@ class SimpleRss < DynamicContent
295
346
  begin
296
347
  o = SimpleRss.create()
297
348
  o.config['url'] = data[:url]
349
+ o.config['url_userid'] = data[:url_userid]
350
+ o.config['url_password'] = data[:url_password]
298
351
  o.config['output_format'] = data[:output_format]
299
352
  o.config['max_items'] = data[:max_items]
300
353
  o.config['reverse_order'] = data[:reverse_order]
@@ -6,9 +6,26 @@
6
6
  <%= config.label :url %>
7
7
  <div class="input">
8
8
  <%= config.url_field :url, :placeholder => 'http://feeds.bbci.co.uk/news/rss.xml', :class => "input-xxlarge", :value => @content.config['url'], "data-url" => preview_contents_path %>
9
- <div><small>You can verify an RSS feed at <a href="http://validator.w3.org/feed/" target="_blank">http://validator.w3.org/feed/</a></small></div>
10
9
  </div>
11
10
  </div>
11
+
12
+ <div class="row-fluid">
13
+ <div class="span6">
14
+ <div class="clearfix span6">
15
+ <%= label_tooltip "simple_rss", :url_userid, t("activerecord.attributes.simple_rss.config_url_userid"), tip: t("basic_auth_tip") %>
16
+ <div class="input">
17
+ <%= config.text_field :url_userid, :class => "input-medium", autocomplete: :off %>
18
+ </div>
19
+ </div>
20
+ <div class="clearfix span6">
21
+ <%= config.label :url_password, t("activerecord.attributes.simple_rss.config_url_password") %>
22
+ <div class="input">
23
+ <%= config.password_field :url_password, :class => "input-medium", autocomplete: :off %>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+
12
29
  <div class="clearfix">
13
30
  <%= config.label :output_format, 'Display Format' %>
14
31
  <div class="input">
@@ -0,0 +1,7 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ simple_rss:
5
+ config_url_userid: "Basic Auth Id"
6
+ config_url_password: "Password"
7
+ basic_auth_tip: "Leave the User Id and Password blank if the URL does not require Basic HTTP Authentication."
@@ -1,9 +1,13 @@
1
1
  module ConcertoSimpleRss
2
2
  class Engine < ::Rails::Engine
3
+ require 'encryptor'
4
+
3
5
  isolate_namespace ConcertoSimpleRss
4
6
 
5
7
  initializer "register content type" do |app|
6
8
  app.config.content_types << SimpleRss
9
+
10
+ Encryptor.default_options.merge!(key: ENV["SECRET_KEY_BASE"])
7
11
  end
8
12
  end
9
13
  end
@@ -1,3 +1,3 @@
1
1
  module ConcertoSimpleRss
2
- VERSION = "0.7"
2
+ VERSION = "0.8"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concerto_simple_rss
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.7'
4
+ version: '0.8'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Michalski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-10 00:00:00.000000000 Z
11
+ date: 2015-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: encryptor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: ruby-xslt
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -57,6 +71,7 @@ files:
57
71
  - app/views/contents/simple_rss/_render_grid.html.erb
58
72
  - app/views/contents/simple_rss/_render_tile.html.erb
59
73
  - app/views/contents/simple_rss/_tab_icon.html.erb
74
+ - config/locales/en.yml
60
75
  - config/routes.rb
61
76
  - lib/concerto_simple_rss.rb
62
77
  - lib/concerto_simple_rss/engine.rb
@@ -113,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
128
  version: '0'
114
129
  requirements: []
115
130
  rubyforge_project:
116
- rubygems_version: 2.4.5
131
+ rubygems_version: 2.2.3
117
132
  signing_key:
118
133
  specification_version: 4
119
134
  summary: RSS Dynamic Concerto for Concerto 2.