amiba 0.0.2
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/Gemfile +28 -0
- data/Thorfile +2 -0
- data/bin/amiba +15 -0
- data/bin/amiba-wp-import +113 -0
- data/lib/amiba/all.rb +13 -0
- data/lib/amiba/configuration.rb +40 -0
- data/lib/amiba/core_ext/file.rb +7 -0
- data/lib/amiba/core_ext/symbol.rb +11 -0
- data/lib/amiba/entry.rb +108 -0
- data/lib/amiba/page.rb +139 -0
- data/lib/amiba/repo.rb +28 -0
- data/lib/amiba/reverse_markdown.rb +199 -0
- data/lib/amiba/scope.rb +49 -0
- data/lib/amiba/site.rb +166 -0
- data/lib/amiba/source/entry.rb +42 -0
- data/lib/amiba/source/entry_finder.rb +136 -0
- data/lib/amiba/source/feed.rb +34 -0
- data/lib/amiba/source/partial.rb +22 -0
- data/lib/amiba/source.rb +148 -0
- data/lib/amiba.rb +90 -0
- data/templates/.amiba +4 -0
- data/templates/entries/.empty_directory +0 -0
- data/templates/layouts/default.haml.tt +6 -0
- data/templates/pages/.empty_directory +0 -0
- data/templates/skeletons/page.haml.tt +9 -0
- data/templates/skeletons/page.md.tt +8 -0
- metadata +282 -0
data/Gemfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "thor", "~>0.14.6", :require => %w{thor thor/group thor/runner}
|
4
|
+
gem "tilt", "~>1.2.1"
|
5
|
+
gem "haml", "~>3.0.25"
|
6
|
+
gem "activesupport", "~>3.0.4", :require => 'active_support/all'
|
7
|
+
gem "activemodel", "~>3.0.3", :require => 'active_model'
|
8
|
+
gem "i18n", "~> 0.5.0"
|
9
|
+
gem "rdiscount", "~> 1.6.8"
|
10
|
+
gem "grit", "~> 2.4.1"
|
11
|
+
gem "yajl-ruby", :require => "yajl/json_gem"
|
12
|
+
gem "fog", ">= 0.6.0"
|
13
|
+
|
14
|
+
group :development do
|
15
|
+
gem "rspec"
|
16
|
+
gem "rspec_tag_matchers"
|
17
|
+
gem "autotest"
|
18
|
+
gem "factory_girl", ">=2.0.0.beta1"
|
19
|
+
gem "ruby-debug19", :require => "ruby-debug"
|
20
|
+
end
|
21
|
+
|
22
|
+
group :wp do
|
23
|
+
gem "sequel"
|
24
|
+
end
|
25
|
+
|
26
|
+
if ENV['AMIBA_BIN'] == 'true'
|
27
|
+
gem 'amiba', :require => %w{amiba amiba/all}
|
28
|
+
end
|
data/Thorfile
ADDED
data/bin/amiba
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby -*-
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
if !File.exist?('Gemfile')
|
6
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
ENV["AMIBA_BIN"] = "true"
|
10
|
+
|
11
|
+
require 'bundler/setup'
|
12
|
+
Bundler.require(:default, :production)
|
13
|
+
|
14
|
+
$thor_runner = true
|
15
|
+
Thor::Runner.start
|
data/bin/amiba-wp-import
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- mode: ruby -*-
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
if !File.exist?('Gemfile')
|
6
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
ENV["AMIBA_BIN"] = "true"
|
10
|
+
|
11
|
+
IMAGE_ROOT="http://wp-uploads.s3.amazonaws.com/wp-content/uploads/"
|
12
|
+
|
13
|
+
require 'bundler/setup'
|
14
|
+
Bundler.require(:default, :production, :wp)
|
15
|
+
|
16
|
+
require 'optparse'
|
17
|
+
require 'amiba/reverse_markdown'
|
18
|
+
|
19
|
+
require 'cgi'
|
20
|
+
require 'uri'
|
21
|
+
|
22
|
+
include Amiba::Repo
|
23
|
+
|
24
|
+
options = {}
|
25
|
+
options[:dbtype] = "mysql"
|
26
|
+
|
27
|
+
OptionParser.new do |opt|
|
28
|
+
opt.on("--dbuser [USER]", "The database user to connect as") do |u|
|
29
|
+
options[:dbuser] = u
|
30
|
+
end
|
31
|
+
opt.on("--dbtype [TYPE]", "The type of database to connect to. Default is MySQL") do |u|
|
32
|
+
options[:dbtype] = u
|
33
|
+
end
|
34
|
+
opt.on("--dbhost [HOST]", "The database host to connect to") do |u|
|
35
|
+
options[:dbhost] = u
|
36
|
+
end
|
37
|
+
opt.on("--db [NAME]", "The database to connect to") do |u|
|
38
|
+
options[:db] = u
|
39
|
+
end
|
40
|
+
opt.on("--dbpass [PASS]", "The database password to connect with") do |u|
|
41
|
+
options[:dbpass] = u
|
42
|
+
end
|
43
|
+
opt.on("--target [TARGET]", "The directory to create an amiba structure in") do |u|
|
44
|
+
options[:target] = u
|
45
|
+
end
|
46
|
+
|
47
|
+
end.parse!
|
48
|
+
|
49
|
+
path, name = File.split options[:target]
|
50
|
+
if ! system "amiba create #{name} --path #{path}"
|
51
|
+
puts "Couldn't create a basic amiba site at #{options[:target]}!"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
|
55
|
+
Dir.chdir options[:target]
|
56
|
+
|
57
|
+
|
58
|
+
DB = Sequel.connect(:adapter=>options[:dbtype], :host => options[:dbhost], :database=>options[:db], :user => options[:dbuser], :password => options[:dbpass], :encoding => 'utf8')
|
59
|
+
Sequel::MySQL.convert_invalid_date_time = nil
|
60
|
+
|
61
|
+
DB[:wp_posts].filter(:post_type=>"post").each do |post|
|
62
|
+
rm = Amiba::ReverseMarkdown.new
|
63
|
+
meta = {}
|
64
|
+
category = DB[:wp_terms].select(:name).join(:wp_term_relationships, :term_taxonomy_id=>:term_id).filter(:object_id=>post[:ID]).first
|
65
|
+
next unless category
|
66
|
+
|
67
|
+
meta[:author] = DB[:wp_users].select(:display_name).filter(:ID=>post[:post_author]).first[:display_name]
|
68
|
+
meta[:slug] = post[:post_excerpt].to_s
|
69
|
+
meta[:layout] = 'default'
|
70
|
+
meta[:state] = post[:post_status] == "publish" ? "published" : "draft"
|
71
|
+
meta[:title] = post[:post_title].to_s
|
72
|
+
meta[:id] = post[:ID]
|
73
|
+
|
74
|
+
#yet more assumptions (like the fact that an attached file is an
|
75
|
+
#image!)
|
76
|
+
attach = DB[:wp_postmeta].select(:meta_value).filter(:post_id=>DB[:wp_posts].select(:ID).filter({:ID=>post[:ID]} | {:post_parent=>post[:ID]})).filter(:meta_key=>"_wp_attached_file").first
|
77
|
+
|
78
|
+
meta[:image] = URI.join(IMAGE_ROOT, attach[:meta_value]).to_s if attach
|
79
|
+
|
80
|
+
# FIXME: (or just run away) kludge to get round UTF-8 madness - just
|
81
|
+
# turn UTF-8 raw chars into form encoded equivalent, then cgi unescape
|
82
|
+
if post[:post_content].encoding.name == "ASCII-8BIT"
|
83
|
+
content = CGI.unescape(post[:post_content].to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) { '%'+$1.unpack('H2'*$1.bytesize).join('%').upcase }).gsub(/(\w+\s?)&(\s?\w+)/,'\1&\2')
|
84
|
+
else
|
85
|
+
content = post[:post_content].gsub(/\s(\w+\s?)&(\s?\w+)/,'\1 & \2')
|
86
|
+
end
|
87
|
+
begin
|
88
|
+
md = rm.parse_string("<p>#{content}</p>")
|
89
|
+
rescue
|
90
|
+
puts "Failed to parse content for ID: #{post[:ID]}: #{$!}"
|
91
|
+
end
|
92
|
+
|
93
|
+
entry = Amiba::Source::Entry.new(category[:name].to_s, post[:post_name], 'markdown', meta, md.nil? ? post[:post_content].to_s : md)
|
94
|
+
|
95
|
+
unless entry.valid?
|
96
|
+
str = ""
|
97
|
+
entry.errors.each_pair do |area, msg|
|
98
|
+
if msg.is_a? Array
|
99
|
+
msg.each {|m| str += "Error detected in #{area}: #{m}\n" }
|
100
|
+
else
|
101
|
+
str += "Error detected in #{area}: #{msg.to_s}\n"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
puts str
|
105
|
+
end
|
106
|
+
|
107
|
+
entry.save do |file, data|
|
108
|
+
FileUtils.mkdir_p File.dirname(file)
|
109
|
+
File.open(file, 'w') { |f| f.write(data) }
|
110
|
+
add_and_commit file
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
data/lib/amiba/all.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'amiba/core_ext/file'
|
2
|
+
require 'amiba/core_ext/symbol'
|
3
|
+
require 'amiba/configuration'
|
4
|
+
require 'amiba/repo'
|
5
|
+
require 'amiba/source'
|
6
|
+
require 'amiba/source/entry_finder'
|
7
|
+
require 'amiba/source/entry'
|
8
|
+
require 'amiba/source/feed'
|
9
|
+
require 'amiba/source/partial'
|
10
|
+
require 'amiba/page'
|
11
|
+
require 'amiba/entry'
|
12
|
+
require 'amiba/site'
|
13
|
+
require 'amiba/scope'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
|
3
|
+
module Amiba
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def method_missing(name, *args, &block)
|
9
|
+
raise ArgumentError if args.length > 1
|
10
|
+
if name.to_s[-1] == '='
|
11
|
+
write_setting(name[0..-2].to_sym, args[0])
|
12
|
+
else
|
13
|
+
read_setting(name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def load_defaults
|
20
|
+
defaults = YAML.load(File.read('.amiba'))
|
21
|
+
@config.merge!(defaults)
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_setting(name, value)
|
25
|
+
config[name] = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_setting(name)
|
29
|
+
config[name]
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
return @config unless @config.nil?
|
34
|
+
@config = HashWithIndifferentAccess.new
|
35
|
+
load_defaults
|
36
|
+
@config
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/amiba/entry.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
module Amiba
|
2
|
+
module Entry
|
3
|
+
|
4
|
+
class Create < Thor::Group
|
5
|
+
include Amiba::Generator
|
6
|
+
include Amiba::Repo
|
7
|
+
|
8
|
+
namespace :"entry:create"
|
9
|
+
argument :format, :default => 'markdown'
|
10
|
+
class_option :category, :required => true
|
11
|
+
class_option :title, :required => true
|
12
|
+
class_option :state, :default => 'draft'
|
13
|
+
class_option :layout, :default => 'default'
|
14
|
+
class_option :slug
|
15
|
+
|
16
|
+
def init_source
|
17
|
+
@source = Amiba::Source::Entry.new(options[:category].to_sym,
|
18
|
+
name,
|
19
|
+
format,
|
20
|
+
options,
|
21
|
+
"h1. New post\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def should_not_exist
|
25
|
+
unless @source.new?
|
26
|
+
raise Thor::Error.new("Error: An entry called '#{name}' already exists.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def should_be_valid
|
31
|
+
unless @source.valid?
|
32
|
+
str = ""
|
33
|
+
@source.errors.each_pair do |area, msg|
|
34
|
+
if msg.is_a? Array
|
35
|
+
msg.each {|m| str += "Error detected in #{area}: #{m}\n" }
|
36
|
+
else
|
37
|
+
str += "Error detected in #{area}: #{msg.to_s}\n"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
raise Thor::Error.new("Errors detected:\n" + str)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def save_entry
|
45
|
+
@source.save do |filename, file_data|
|
46
|
+
create_file filename, file_data
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_to_git
|
51
|
+
add_and_commit @source.filename
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
no_tasks do
|
57
|
+
def name
|
58
|
+
options[:title].parameterize
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Thor task to mark an entry published.
|
64
|
+
class Publish < Thor::Group
|
65
|
+
include Amiba::Generator
|
66
|
+
include Amiba::Repo
|
67
|
+
|
68
|
+
namespace :"entry:publish"
|
69
|
+
argument :name
|
70
|
+
argument :format, :default => 'markdown'
|
71
|
+
class_option :category, :required => true
|
72
|
+
|
73
|
+
def init_source
|
74
|
+
@source = Amiba::Source::Entry.new(options[:category].to_sym, name, format)
|
75
|
+
end
|
76
|
+
|
77
|
+
def should_exist
|
78
|
+
if @source.new?
|
79
|
+
raise Thor::Error.new("Error: Can't publish an entry that doesn't exist.")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def should_not_be_published
|
84
|
+
if @source.state == "published"
|
85
|
+
raise Thor::Error.new("Entry already published")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def save_page
|
90
|
+
@source.state = "published"
|
91
|
+
@source.save do |filename, file_data|
|
92
|
+
remove_file filename, :verbose => false
|
93
|
+
create_file(filename, :verbose => false) do
|
94
|
+
file_data
|
95
|
+
end
|
96
|
+
say_status :published, filename, :green
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_to_git
|
101
|
+
add_and_commit @source.filename, "Published #{@source.filename}"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/lib/amiba/page.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'amiba/source'
|
2
|
+
|
3
|
+
module Amiba
|
4
|
+
module Page
|
5
|
+
|
6
|
+
# Thor task to create a new page. It checks for the existance of a page already existing
|
7
|
+
# and that the user specified a valid format before progressing.
|
8
|
+
class Create < Thor::Group
|
9
|
+
include Amiba::Generator
|
10
|
+
include Amiba::Repo
|
11
|
+
|
12
|
+
namespace :"page:create"
|
13
|
+
argument :name
|
14
|
+
argument :format, :default => "haml"
|
15
|
+
class_option :layout, :default =>"default"
|
16
|
+
class_option :title, :required => true
|
17
|
+
class_option :description, :required => true
|
18
|
+
class_option :category, :default => "plain"
|
19
|
+
class_option :state, :default => "draft"
|
20
|
+
|
21
|
+
def init_source
|
22
|
+
@source = Amiba::Source::Page.new(name, format, options, Templates.send(format.to_sym))
|
23
|
+
end
|
24
|
+
|
25
|
+
def should_not_exist
|
26
|
+
unless @source.new?
|
27
|
+
raise Thor::Error.new("Error:A page called '#{name}' has already been created.")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def should_be_correct_format
|
32
|
+
if !@source.valid? && !@source.errors[:format].nil?
|
33
|
+
raise Thor::Error.new("Error: format should be one of " +
|
34
|
+
Amiba::Source::Page::VALID_FORMATS.join(','))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_page
|
39
|
+
@source.save do |filename, file_data|
|
40
|
+
create_file filename, file_data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_to_git
|
45
|
+
add_and_commit @source.filename
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Thor task to mark a page published.
|
51
|
+
class Publish < Thor::Group
|
52
|
+
include Amiba::Generator
|
53
|
+
include Amiba::Repo
|
54
|
+
|
55
|
+
namespace :"page:publish"
|
56
|
+
argument :name
|
57
|
+
argument :format, :default => 'haml'
|
58
|
+
|
59
|
+
def init_source
|
60
|
+
@source = Amiba::Source::Page.new(name, format)
|
61
|
+
end
|
62
|
+
|
63
|
+
def should_exist
|
64
|
+
if @source.new?
|
65
|
+
raise Thor::Error.new("Error: Can't publish a page that doesn't exist.")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def should_not_be_published
|
70
|
+
if @source.state == "published"
|
71
|
+
raise Thor::Error.new("Page already published")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def save_page
|
76
|
+
@source.state = "published"
|
77
|
+
@source.save do |filename, file_data|
|
78
|
+
remove_file filename, :verbose => false
|
79
|
+
create_file(filename, :verbose => false) do
|
80
|
+
file_data
|
81
|
+
end
|
82
|
+
say_status :published, filename, :green
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_to_git
|
87
|
+
add_and_commit @source.filename, "Published #{@source.filename}"
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Thor task to destroy a page. It will delete all files matching the page name
|
93
|
+
class Destroy < Thor::Group
|
94
|
+
include Amiba::Generator
|
95
|
+
|
96
|
+
namespace :"page:destroy"
|
97
|
+
argument :name
|
98
|
+
argument :format, :default => 'haml'
|
99
|
+
|
100
|
+
def init_source
|
101
|
+
@source = Amiba::Source::Page.new(name, format)
|
102
|
+
end
|
103
|
+
|
104
|
+
def page
|
105
|
+
if ask("Are you sure you want to delete #{@source.filename}?" +
|
106
|
+
" This is irreversible (y/n): ")
|
107
|
+
remove_file(@source.filename)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# Lists all pages currently managed by this Amiba project
|
114
|
+
class List < Thor::Group
|
115
|
+
include Amiba::Generator
|
116
|
+
|
117
|
+
namespace :"page:list"
|
118
|
+
|
119
|
+
def list
|
120
|
+
Dir.glob("pages/*").each {|p| say File.basename(p)}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Hate this - will deprecate as soon as I think of a more elegant solution
|
126
|
+
class Templates
|
127
|
+
class << self
|
128
|
+
def haml
|
129
|
+
"%h1 Title\n%p Body\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
def markdown
|
133
|
+
"# Title #\nBody\n"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
data/lib/amiba/repo.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Amiba
|
2
|
+
module Repo
|
3
|
+
|
4
|
+
def init(dir)
|
5
|
+
Grit::Repo.init(dir)
|
6
|
+
end
|
7
|
+
|
8
|
+
def repo
|
9
|
+
Grit::Repo.new(Dir.pwd)
|
10
|
+
rescue
|
11
|
+
raise "No repo exists at #{Dir.pwd}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_and_commit(filename, msg=nil)
|
15
|
+
repo.add(filename)
|
16
|
+
repo.commit_index(msg || "Added a new entry at #{filename}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_commit_date(filename)
|
20
|
+
repo.log(filename).first.committed_date
|
21
|
+
end
|
22
|
+
|
23
|
+
def last_commit_dates(*filenames)
|
24
|
+
filenames.map {|fn| last_commit_date(fn)}
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|