amiba 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|