spree_simple_blog 3.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/README.markdown +6 -0
- data/app/controllers/articles_controller.rb +30 -0
- data/app/helpers/articles_helper.rb +37 -0
- data/app/models/article.rb +53 -0
- data/app/models/file_model.rb +98 -0
- data/app/views/articles/_archives.html.erb +8 -0
- data/app/views/articles/_article.html.erb +31 -0
- data/app/views/articles/_comment_count_js.html.erb +18 -0
- data/app/views/articles/_recent.html.erb +8 -0
- data/app/views/articles/_sidebar.html.erb +11 -0
- data/app/views/articles/_subscribe.html.erb +6 -0
- data/app/views/articles/index.html.erb +30 -0
- data/app/views/articles/index.xml.builder +20 -0
- data/app/views/articles/show.html.erb +38 -0
- data/lib/spree_simple_blog.rb +22 -0
- data/lib/tasks/blog.rake +30 -0
- data/lib/tasks/simple_blog_extension_tasks.rake +17 -0
- metadata +117 -0
data/README.markdown
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
= Spree Simple Blog
|
2
|
+
|
3
|
+
Adds a simple blog to Spree which works much like Toto (http://github.com/cloudhead/toto/), reading text files in /content/articles. Content files consist of
|
4
|
+
yaml metadata followed by Markdown content.
|
5
|
+
|
6
|
+
By extending FileModel, other types of content can be managed in the same way way.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class ArticlesController < Spree::BaseController
|
2
|
+
rescue_from Errno::ENOENT, :with => :render_404
|
3
|
+
|
4
|
+
def index
|
5
|
+
@per_page = 10
|
6
|
+
@current_page = (params[:page] || 1).to_i
|
7
|
+
@total_pages = (Article.count / @per_page.to_f).ceil
|
8
|
+
@articles = Article.find_articles(:page => @current_page)
|
9
|
+
respond_to do |wants|
|
10
|
+
wants.html {}
|
11
|
+
wants.xml {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def archive
|
16
|
+
@articles = Article.find_articles(:year => params[:year], :month => params[:month])
|
17
|
+
render :action => 'index'
|
18
|
+
end
|
19
|
+
|
20
|
+
def tag
|
21
|
+
@articles = Article.tagged(params[:tag])
|
22
|
+
render :action => 'index'
|
23
|
+
end
|
24
|
+
|
25
|
+
def show
|
26
|
+
key = "#{params[:year]}-#{params[:month].rjust(2,'0')}-#{params[:day].rjust(2,'0')}-#{params[:permalink]}"
|
27
|
+
@article = Article.find(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rdiscount'
|
2
|
+
|
3
|
+
module ArticlesHelper
|
4
|
+
|
5
|
+
def html_transform(text)
|
6
|
+
RDiscount.new(text.to_s).to_html.html_safe
|
7
|
+
end
|
8
|
+
|
9
|
+
def format_date(time)
|
10
|
+
time.strftime("%B %d %Y")
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_time(time)
|
14
|
+
time.strftime("%I:%m%p")
|
15
|
+
end
|
16
|
+
|
17
|
+
def link_to_archive(archive_month)
|
18
|
+
text = "#{Date::MONTHNAMES[archive_month.last.to_i]} #{archive_month.first.to_i}"
|
19
|
+
link_to text, blog_articles_archive_url(:year => archive_month.first, :month => archive_month.last.to_s.rjust(2, '0'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def link_to_article(article, options = {})
|
23
|
+
link_to h(article.title), article_url(article), options
|
24
|
+
end
|
25
|
+
|
26
|
+
def article_url(article)
|
27
|
+
blog_article_url :permalink => article.slug, :year => article.date.year, :month => article.date.month, :day => article.date.day
|
28
|
+
end
|
29
|
+
|
30
|
+
def url_escape(string)
|
31
|
+
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
32
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
33
|
+
end.tr(' ', '+')
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Article < FileModel
|
2
|
+
|
3
|
+
def self.find_articles(options = {})
|
4
|
+
options = {
|
5
|
+
:per_page => 10,
|
6
|
+
:page => 1
|
7
|
+
}.merge(options)
|
8
|
+
|
9
|
+
options[:offset] = (options[:page]-1) * options[:per_page]
|
10
|
+
options[:limit] = options[:per_page]
|
11
|
+
|
12
|
+
if options[:year] and options[:month]
|
13
|
+
options[:match] = %r(\/#{options[:year]}-#{options[:month].to_s.rjust(2,'0')})
|
14
|
+
end
|
15
|
+
|
16
|
+
all(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.recent(number = 5)
|
20
|
+
@@recent ||= all(:limit => number)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.tagged(tag)
|
24
|
+
tag_index[tag.strip].to_a.map do |key|
|
25
|
+
Article.find(key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.archive_months
|
30
|
+
@@archive_months ||= files.map{|f| File.basename(f).split('-')[0..1] }.uniq
|
31
|
+
end
|
32
|
+
|
33
|
+
# Store list of articles for each tag in a hash
|
34
|
+
# Fast enough for the small number of articles we're dealing with
|
35
|
+
@@tag_index = {}
|
36
|
+
def self.tag_index
|
37
|
+
return @@tag_index if @@tag_index.present?
|
38
|
+
all.each do |a|
|
39
|
+
a.tags.split(',').map(&:strip).each do |t|
|
40
|
+
@@tag_index[t] ||= []
|
41
|
+
@@tag_index[t] = (@@tag_index[t] << a.key).uniq
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@@tag_index
|
45
|
+
end
|
46
|
+
|
47
|
+
%w(title_tag meta_keywords meta_description).each do |attribute|
|
48
|
+
define_method(attribute) do
|
49
|
+
meta[attribute]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# Base class for simple models that load their content from text files formatted like Toto's articles
|
2
|
+
class FileModel
|
3
|
+
attr_accessor :file, :key, :meta, :body
|
4
|
+
|
5
|
+
def initialize(file, key, meta, body)
|
6
|
+
@file = file
|
7
|
+
@key = key
|
8
|
+
@meta = meta
|
9
|
+
@body = body
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method, *args)
|
13
|
+
if meta.has_key?(method.to_s)
|
14
|
+
meta[method.to_s]
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def self.all(options = {})
|
22
|
+
options = {
|
23
|
+
:offset => 0
|
24
|
+
}.merge(options)
|
25
|
+
|
26
|
+
files = get_filtered_files(options)
|
27
|
+
|
28
|
+
if options[:limit]
|
29
|
+
files = files[options[:offset] .. (options[:offset].to_i + options[:limit].to_i-1)]
|
30
|
+
end
|
31
|
+
|
32
|
+
records = files.to_a.map do |f|
|
33
|
+
from_file(f)
|
34
|
+
end
|
35
|
+
|
36
|
+
records
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.find(key)
|
40
|
+
from_file("#{content_path}/#{key}.txt")
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.count(options = {})
|
44
|
+
get_filtered_files(options).length
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.random(options = {})
|
48
|
+
from_file(files[rand(all.size)])
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.files
|
52
|
+
Dir["#{content_path}/*.txt"].sort_by {|f| File.basename(f) }.reverse
|
53
|
+
end
|
54
|
+
|
55
|
+
def index
|
56
|
+
@index ||= self.class.files.index(file)
|
57
|
+
end
|
58
|
+
|
59
|
+
def next
|
60
|
+
return @next if @next.present?
|
61
|
+
if next_file = self.class.files[index + 1]
|
62
|
+
@next = self.class.from_file(next_file)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def previous
|
67
|
+
return @previous if @previous.present?
|
68
|
+
if index > 0 and previous_file = self.class.files[index - 1]
|
69
|
+
@previous = self.class.from_file(previous_file)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.from_file(file)
|
74
|
+
meta, body = File.read(file).split(/\n\n/, 2)
|
75
|
+
meta = YAML.load(meta)
|
76
|
+
key = File.basename(file).gsub('.txt','')
|
77
|
+
new(file, key, meta, body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.content_path
|
81
|
+
File.expand_path(File.join(Rails.root, "content", relative_path))
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.relative_path
|
85
|
+
self.name.underscore
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def self.get_filtered_files(options)
|
91
|
+
if options[:match]
|
92
|
+
files.select{|f| f =~ options[:match]}
|
93
|
+
else
|
94
|
+
files
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<div class="post">
|
2
|
+
|
3
|
+
<div class="heading">
|
4
|
+
<h2><%= link_to_article article %></h2>
|
5
|
+
<p>
|
6
|
+
<span class="author">by <strong><%= article.author %></strong></span>
|
7
|
+
•
|
8
|
+
<span class="date"><%= format_date article.date %></span>
|
9
|
+
</p>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="entry">
|
13
|
+
|
14
|
+
<%= html_transform article.body %>
|
15
|
+
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<% if @articles %>
|
19
|
+
<div class="meta">
|
20
|
+
<p class="comments"><a href="<%= article_url article %>#respond" title="Comment on <%= article.title %>">Comments »</a></p>
|
21
|
+
<p class="time">Posted @ <%= format_time article.date %></p>
|
22
|
+
<br class="clear" />
|
23
|
+
</div>
|
24
|
+
<% else %>
|
25
|
+
<% if @article.tags.present? %>
|
26
|
+
<p>Tags:
|
27
|
+
<%= @article.tags.split(',').map { |tag| link_to(tag, blog_tag_path(tag), :rel => 'tag')}.join(', ') %>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<% if Spree::Config[:disqus_id] %>
|
2
|
+
<% content_for :extra_javascripts do %>
|
3
|
+
<script type="text/javascript">
|
4
|
+
//<![CDATA[
|
5
|
+
(function() {
|
6
|
+
var links = document.getElementsByTagName('a');
|
7
|
+
var query = '?';
|
8
|
+
for(var i = 0; i < links.length; i++) {
|
9
|
+
if(links[i].href.indexOf('#disqus_thread') >= 0) {
|
10
|
+
query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&';
|
11
|
+
}
|
12
|
+
}
|
13
|
+
document.write('<script charset="utf-8" type="text/javascript" src="http://disqus.com/forums/<%= Spree::Config[:disqus_id] %>/get_num_replies.js' + query + '"></' + 'script>');
|
14
|
+
})();
|
15
|
+
//]]>
|
16
|
+
</script>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<% self.title = "Blog" %>
|
2
|
+
<% @body_class = 'blog' %>
|
3
|
+
|
4
|
+
<% if params[:tag] %>
|
5
|
+
<h2 class="pagetitle">Posts Tagged ‘<%= params[:tag] %>’</h2>
|
6
|
+
<% elsif params[:year] && params[:month] %>
|
7
|
+
<h2 class="pagetitle">Archive for <%= Date::MONTHNAMES[params[:month].to_i] %>, <%= params[:year].to_i %></h2>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<%= render :partial => 'article', :collection => @articles %>
|
11
|
+
|
12
|
+
<% if @total_pages %>
|
13
|
+
<div class="navigation">
|
14
|
+
<% if @total_pages > 1 && @current_page < @total_pages %>
|
15
|
+
<div class="alignleft"><a href="<%= url_for(:overwrite_params => {:page => @current_page + 1}) %>" >« Older Entries</a></div>
|
16
|
+
<% end %>
|
17
|
+
<% if @current_page > 1 %>
|
18
|
+
<div class="alignright"><a href="<%= url_for(:overwrite_params => {:page => @current_page - 1}) %>" >Newer Entries »</a></div>
|
19
|
+
<% end %>
|
20
|
+
<br class="clear" />
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% content_for :sidebar do %>
|
25
|
+
|
26
|
+
<%= render 'sidebar' %>
|
27
|
+
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
<br class="clear" />
|
@@ -0,0 +1,20 @@
|
|
1
|
+
xml.instruct!
|
2
|
+
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
|
3
|
+
xml.title Spree::Config[:blog_feed_title]
|
4
|
+
xml.id Spree::Config[:blog_feed_url]
|
5
|
+
xml.updated @articles.first.date.xmlschema unless @articles.empty?
|
6
|
+
xml.author { xml.name Spree::Config[:blog_feed_author] }
|
7
|
+
|
8
|
+
@articles.each do |article|
|
9
|
+
xml.entry do
|
10
|
+
xml.title article.title
|
11
|
+
xml.link "rel" => "alternate", "href" => article_url(article)
|
12
|
+
xml.id article_url(article)
|
13
|
+
xml.published article.date.xmlschema
|
14
|
+
xml.updated article.date.xmlschema
|
15
|
+
xml.author { xml.name article.author }
|
16
|
+
xml.content("type" => "html") { xml.cdata!(html_transform(article.body)) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<% self.title = "Blog - " + @article.title %>
|
2
|
+
<% @body_class = 'blog' %>
|
3
|
+
|
4
|
+
<div class="navigation">
|
5
|
+
<% if @article.previous %>
|
6
|
+
<div class="alignleft">« <%= link_to_article @article.previous %></div>
|
7
|
+
<% end %>
|
8
|
+
<% if @article.next %>
|
9
|
+
<div class="alignright"><%= link_to_article @article.next %> »</div>
|
10
|
+
<% end %>
|
11
|
+
<br class="clear" />
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<%= render 'article', :article => @article %>
|
15
|
+
|
16
|
+
<% if Spree::Config[:disqus_id] %>
|
17
|
+
<div id="comments">
|
18
|
+
<a name="respond"></a>
|
19
|
+
<div id="disqus_thread"></div>
|
20
|
+
<script type="text/javascript">
|
21
|
+
var disqus_developer = <%= RAILS_ENV == 'development' ? '1' : '0' %>;
|
22
|
+
</script>
|
23
|
+
<noscript><a href="http://spreecommerce.disqus.com/?url=ref">View the discussion thread.</a></noscript>
|
24
|
+
<a href="http://disqus.com" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
<% content_for :sidebar do %>
|
31
|
+
<%= render 'sidebar' %>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% if Spree::Config[:disqus_id] %>
|
35
|
+
<% content_for :extra_javascripts do %>
|
36
|
+
<script type="text/javascript" src="http://disqus.com/forums/<%= Spree::Config[:disqus_id] %>/embed.js"></script>
|
37
|
+
<% end %>
|
38
|
+
<% end %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SimpleBlogExtension
|
2
|
+
class Engine < Rails::Engine
|
3
|
+
|
4
|
+
def self.activate
|
5
|
+
|
6
|
+
Spree::BaseController.class_eval do
|
7
|
+
helper ArticlesHelper
|
8
|
+
end
|
9
|
+
|
10
|
+
AppConfiguration.class_eval do
|
11
|
+
preference :disqus_id, :default => nil
|
12
|
+
preference :blog_feed_url, :default => '/blog.xml'
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
config.autoload_paths += %W(#{config.root}/lib)
|
19
|
+
config.to_prepare &method(:activate).to_proc
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/lib/tasks/blog.rake
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
namespace :blog do
|
2
|
+
|
3
|
+
task :new => :environment do
|
4
|
+
|
5
|
+
title = ask('Title: ')
|
6
|
+
slug = title.empty?? nil : title.strip.parameterize.to_s
|
7
|
+
|
8
|
+
article = {'slug' => slug, 'title' => title, 'author' => 'Author', 'tags' => '', 'date' => Date.today}.to_yaml
|
9
|
+
article << "\n"
|
10
|
+
article << "Lorem ipsum dolor sit amet...\n\n"
|
11
|
+
|
12
|
+
path = "#{RAILS_ROOT}/content/articles/#{Time.now.strftime("%Y-%m-%d")}#{'-' + slug if slug}.txt"
|
13
|
+
|
14
|
+
unless File.exist? path
|
15
|
+
File.open(path, "w") do |file|
|
16
|
+
file.write article
|
17
|
+
end
|
18
|
+
puts "an article was created for you at #{path}."
|
19
|
+
else
|
20
|
+
puts "I can't create the article, #{path} already exists."
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def ask message
|
28
|
+
print message
|
29
|
+
STDIN.gets.chomp
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
namespace :spree do
|
2
|
+
namespace :extensions do
|
3
|
+
namespace :simple_blog do
|
4
|
+
desc "Copies public assets of the Simple Blog to the instance public/ directory."
|
5
|
+
task :update => :environment do
|
6
|
+
is_svn_git_or_dir = proc {|path| path =~ /\.svn/ || path =~ /\.git/ || File.directory?(path) }
|
7
|
+
Dir[SimpleBlogExtension.root + "/public/**/*"].reject(&is_svn_git_or_dir).each do |file|
|
8
|
+
path = file.sub(SimpleBlogExtension.root, '')
|
9
|
+
directory = File.dirname(path)
|
10
|
+
puts "Copying #{path}..."
|
11
|
+
mkdir_p RAILS_ROOT + directory
|
12
|
+
cp file, RAILS_ROOT + path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spree_simple_blog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 5
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 3.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors: []
|
13
|
+
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-09-03 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: spree_core
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: -1848229955
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 30
|
33
|
+
- 0
|
34
|
+
- beta1
|
35
|
+
version: 0.30.0.beta1
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rdiscount
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - "="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 9
|
47
|
+
segments:
|
48
|
+
- 1
|
49
|
+
- 5
|
50
|
+
- 5
|
51
|
+
version: 1.5.5
|
52
|
+
type: :runtime
|
53
|
+
version_requirements: *id002
|
54
|
+
description:
|
55
|
+
email:
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- README.markdown
|
64
|
+
- lib/spree_simple_blog.rb
|
65
|
+
- lib/tasks/blog.rake
|
66
|
+
- lib/tasks/simple_blog_extension_tasks.rake
|
67
|
+
- app/controllers/articles_controller.rb
|
68
|
+
- app/helpers/articles_helper.rb
|
69
|
+
- app/models/article.rb
|
70
|
+
- app/models/file_model.rb
|
71
|
+
- app/views/articles/_archives.html.erb
|
72
|
+
- app/views/articles/_article.html.erb
|
73
|
+
- app/views/articles/_comment_count_js.html.erb
|
74
|
+
- app/views/articles/_recent.html.erb
|
75
|
+
- app/views/articles/_sidebar.html.erb
|
76
|
+
- app/views/articles/_subscribe.html.erb
|
77
|
+
- app/views/articles/index.html.erb
|
78
|
+
- app/views/articles/index.xml.builder
|
79
|
+
- app/views/articles/show.html.erb
|
80
|
+
has_rdoc: true
|
81
|
+
homepage:
|
82
|
+
licenses: []
|
83
|
+
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 57
|
95
|
+
segments:
|
96
|
+
- 1
|
97
|
+
- 8
|
98
|
+
- 7
|
99
|
+
version: 1.8.7
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
requirements:
|
110
|
+
- none
|
111
|
+
rubyforge_project:
|
112
|
+
rubygems_version: 1.3.7
|
113
|
+
signing_key:
|
114
|
+
specification_version: 3
|
115
|
+
summary: Toto style blogging for Spree
|
116
|
+
test_files: []
|
117
|
+
|