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 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
@@ -0,0 +1,2 @@
1
+ $:.unshift(File.expand_path(File.join(Dir.pwd, "lib")))
2
+ require "amiba"
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
@@ -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
@@ -0,0 +1,7 @@
1
+ class File
2
+
3
+ def self.relpath(fn, dir)
4
+ File.join(File.expand_path(fn).split(File::SEPARATOR) - File.expand_path(dir).split(File::SEPARATOR))
5
+ end
6
+
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support/inflector/inflections'
2
+
3
+ class Symbol
4
+ def pluralize
5
+ ActiveSupport::Inflector.pluralize(self.to_s).to_sym
6
+ end
7
+
8
+ def singularize
9
+ ActiveSupport::Inflector.singularize(self.to_s).to_sym
10
+ end
11
+ end
@@ -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