forge 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/Gemfile +24 -0
- data/Gemfile.lock +81 -0
- data/LICENSE +20 -0
- data/README.md +24 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/forge +12 -0
- data/features/create.feature +34 -0
- data/features/link.feature +14 -0
- data/features/step_definitions/forge_steps.rb +38 -0
- data/features/support/env.rb +17 -0
- data/forge.gemspec +135 -0
- data/layouts/config/config.json.erb +13 -0
- data/layouts/config/stylesheet_header.erb +11 -0
- data/layouts/default/functions/functions.php.erb +21 -0
- data/layouts/default/stylesheets/_reset.scss +5 -0
- data/layouts/default/stylesheets/_typography.scss +5 -0
- data/layouts/default/stylesheets/style.css.scss.erb +28 -0
- data/layouts/default/templates/404.php.erb +10 -0
- data/layouts/default/templates/archive.php.erb +11 -0
- data/layouts/default/templates/attachment.php.erb +34 -0
- data/layouts/default/templates/comments.php +2 -0
- data/layouts/default/templates/footer.php +9 -0
- data/layouts/default/templates/header.php.erb +30 -0
- data/layouts/default/templates/index.php +6 -0
- data/layouts/default/templates/page.php +18 -0
- data/layouts/default/templates/partials/loop.php.erb +30 -0
- data/layouts/default/templates/search.php.erb +14 -0
- data/layouts/default/templates/sidebar.php +9 -0
- data/layouts/default/templates/single.php.erb +32 -0
- data/layouts/lib/forge-settings/README.md +43 -0
- data/layouts/lib/forge-settings/classes/settings.php +11 -0
- data/layouts/lib/forge-settings/classes/settings/collection.php +190 -0
- data/layouts/lib/forge-settings/classes/settings/option.php +125 -0
- data/layouts/lib/forge-settings/classes/settings/option/select.php +30 -0
- data/layouts/lib/forge-settings/classes/settings/option/text.php +15 -0
- data/layouts/lib/forge-settings/classes/settings/section.php +56 -0
- data/lib/forge.rb +11 -0
- data/lib/forge/builder.rb +165 -0
- data/lib/forge/cli.rb +86 -0
- data/lib/forge/config.rb +63 -0
- data/lib/forge/error.rb +8 -0
- data/lib/forge/generator.rb +128 -0
- data/lib/forge/guard.rb +57 -0
- data/lib/forge/project.rb +91 -0
- data/lib/forge/version.rb +3 -0
- data/lib/guard/forge/assets.rb +31 -0
- data/lib/guard/forge/config.rb +31 -0
- data/lib/guard/forge/functions.rb +31 -0
- data/lib/guard/forge/templates.rb +28 -0
- data/spec/lib/forge/config_spec.rb +79 -0
- data/spec/lib/forge/project_spec.rb +34 -0
- data/spec/spec_helper.rb +13 -0
- metadata +263 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
abstract class Settings_Option {
|
4
|
+
protected $_name, $_valid_values, $_value, $_type, $_default_value,
|
5
|
+
$_tab, $_label, $_description, $_parent_name, $_validation_function;
|
6
|
+
|
7
|
+
public function name( $name = NULL ) {
|
8
|
+
if ( NULL === $name )
|
9
|
+
return $this->_name;
|
10
|
+
|
11
|
+
$this->_name = $name;
|
12
|
+
return $this;
|
13
|
+
}
|
14
|
+
|
15
|
+
public function valid_values( $valid_values = NULL ) {
|
16
|
+
if ( NULL === $valid_values )
|
17
|
+
return $this->_valid_values;
|
18
|
+
|
19
|
+
$this->_valid_values = $valid_values;
|
20
|
+
return $this;
|
21
|
+
}
|
22
|
+
|
23
|
+
public function value( $value = NULL ) {
|
24
|
+
if ( NULL === $value ) {
|
25
|
+
return $this->_value;
|
26
|
+
}
|
27
|
+
|
28
|
+
$this->_value = $value;
|
29
|
+
return $this;
|
30
|
+
}
|
31
|
+
|
32
|
+
public function type( $type = NULL ) {
|
33
|
+
if ( NULL === $type )
|
34
|
+
return $this->_type;
|
35
|
+
|
36
|
+
$this->_type = $type;
|
37
|
+
return $this;
|
38
|
+
}
|
39
|
+
|
40
|
+
public function default_value( $default_value = NULL ) {
|
41
|
+
if ( NULL === $default_value )
|
42
|
+
return $this->_default_value;
|
43
|
+
|
44
|
+
$this->_default_value = $default_value;
|
45
|
+
return $this;
|
46
|
+
}
|
47
|
+
|
48
|
+
public function tab( $tab = NULL ) {
|
49
|
+
if ( NULL === $tab )
|
50
|
+
return $this->_tab;
|
51
|
+
|
52
|
+
$this->_tab = $tab;
|
53
|
+
return $this;
|
54
|
+
}
|
55
|
+
|
56
|
+
public function label( $label = NULL ) {
|
57
|
+
if ( NULL === $label )
|
58
|
+
return $this->_label;
|
59
|
+
|
60
|
+
$this->_label = $label;
|
61
|
+
return $this;
|
62
|
+
}
|
63
|
+
|
64
|
+
public function description( $description = NULL ) {
|
65
|
+
if ( NULL === $description )
|
66
|
+
return $this->_description;
|
67
|
+
|
68
|
+
$this->_description = $description;
|
69
|
+
return $this;
|
70
|
+
}
|
71
|
+
|
72
|
+
public function parent_name( $parent_name = NULL ) {
|
73
|
+
if ( NULL === $parent_name )
|
74
|
+
return $this->_parent_name;
|
75
|
+
|
76
|
+
$this->_parent_name = $parent_name;
|
77
|
+
return $this;
|
78
|
+
}
|
79
|
+
|
80
|
+
public function section( $section = NULL ) {
|
81
|
+
if ( NULL === $section )
|
82
|
+
return $this->_section;
|
83
|
+
|
84
|
+
$this->_section = $section;
|
85
|
+
return $this;
|
86
|
+
}
|
87
|
+
|
88
|
+
public function validation_function( $validation_function = NULL ) {
|
89
|
+
if ( NULL === $validation_function )
|
90
|
+
return $this->_validation_function;
|
91
|
+
|
92
|
+
$this->_validation_function = $validation_function;
|
93
|
+
return $this;
|
94
|
+
}
|
95
|
+
|
96
|
+
// The HTML ID takes the form 'parentname-optionname'
|
97
|
+
protected function html_id() {
|
98
|
+
return $this->parent_name() . '-' . $this->name();
|
99
|
+
}
|
100
|
+
|
101
|
+
// Name takes the form 'parentname[optionname]'
|
102
|
+
protected function html_name() {
|
103
|
+
return $this->parent_name() . '[' . $this->name() . ']';
|
104
|
+
}
|
105
|
+
|
106
|
+
public function register() {
|
107
|
+
add_settings_field(
|
108
|
+
$this->name(),
|
109
|
+
$this->label(),
|
110
|
+
array( &$this, 'to_html' ),
|
111
|
+
$this->parent_name(),
|
112
|
+
$this->section() );
|
113
|
+
}
|
114
|
+
|
115
|
+
public function validate( $value ) {
|
116
|
+
if ( $validation_function = $this->validation_function() ) {
|
117
|
+
return $validation_function( $value );
|
118
|
+
} else {
|
119
|
+
return $this->standard_validation( $value );
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
abstract public function to_html();
|
124
|
+
abstract protected function standard_validation( $value );
|
125
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
class Settings_Option_Select extends Settings_Option {
|
4
|
+
public function to_html() {
|
5
|
+
$id = $this->html_id();
|
6
|
+
$name = $this->html_name();
|
7
|
+
|
8
|
+
$output = "<select id='$id' name='$name'>";
|
9
|
+
|
10
|
+
foreach ( $this->valid_values() as $value => $text ) {
|
11
|
+
// If this value is selected, mark it so
|
12
|
+
$selected = selected( $this->value(), $value, false );
|
13
|
+
|
14
|
+
$output .= "<option value='$value' $selected >$text</option>";
|
15
|
+
}
|
16
|
+
|
17
|
+
$output .= "</select>";
|
18
|
+
|
19
|
+
echo $output;
|
20
|
+
}
|
21
|
+
|
22
|
+
protected function standard_validation( $value ) {
|
23
|
+
$valid_values = $this->valid_values();
|
24
|
+
if ( array_key_exists( $value, $valid_values ) ) {
|
25
|
+
return $value;
|
26
|
+
}
|
27
|
+
|
28
|
+
return $this->default_value();
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
class Settings_Option_Text extends Settings_Option {
|
4
|
+
public function to_html() {
|
5
|
+
$id = $this->html_id();
|
6
|
+
$name = $this->html_name();
|
7
|
+
$value = $this->value();
|
8
|
+
|
9
|
+
echo "<input type='text' id='$id' name='$name' value='$value' />";
|
10
|
+
}
|
11
|
+
|
12
|
+
protected function standard_validation( $value ) {
|
13
|
+
return trim( $value );
|
14
|
+
}
|
15
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
class Settings_Section {
|
4
|
+
protected $_id, $_title, $_description, $_parent_name;
|
5
|
+
|
6
|
+
public function __construct( $id, $title, $description, $parent_name ) {
|
7
|
+
$this->id( $id );
|
8
|
+
$this->title( $title );
|
9
|
+
$this->description( $description );
|
10
|
+
$this->parent_name( $parent_name );
|
11
|
+
}
|
12
|
+
|
13
|
+
public function id( $id = NULL ) {
|
14
|
+
if ( NULL === $id )
|
15
|
+
return $this->_id;
|
16
|
+
|
17
|
+
$this->_id = $id;
|
18
|
+
return $this;
|
19
|
+
}
|
20
|
+
|
21
|
+
public function title( $title = NULL ) {
|
22
|
+
if ( NULL === $title )
|
23
|
+
return $this->_title;
|
24
|
+
|
25
|
+
$this->_title = $title;
|
26
|
+
return $this;
|
27
|
+
}
|
28
|
+
|
29
|
+
public function description( $description = NULL ) {
|
30
|
+
if ( NULL === $description )
|
31
|
+
return $this->_description;
|
32
|
+
|
33
|
+
$this->_description = $description;
|
34
|
+
return $this;
|
35
|
+
}
|
36
|
+
|
37
|
+
public function parent_name( $parent_name = NULL ) {
|
38
|
+
if ( NULL === $parent_name )
|
39
|
+
return $this->_parent_name;
|
40
|
+
|
41
|
+
$this->_parent_name = $parent_name;
|
42
|
+
return $this;
|
43
|
+
}
|
44
|
+
|
45
|
+
public function description_html() {
|
46
|
+
echo "<p>{$this->description()}</p>";
|
47
|
+
}
|
48
|
+
|
49
|
+
public function register() {
|
50
|
+
add_settings_section(
|
51
|
+
$this->id(),
|
52
|
+
$this->title(),
|
53
|
+
array( &$this, 'description_html' ),
|
54
|
+
$this->parent_name() );
|
55
|
+
}
|
56
|
+
}
|
data/lib/forge.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'forge/error'
|
2
|
+
|
3
|
+
module Forge
|
4
|
+
ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
5
|
+
autoload :Guard, 'forge/guard'
|
6
|
+
autoload :CLI, 'forge/cli'
|
7
|
+
autoload :Project, 'forge/project'
|
8
|
+
autoload :Builder, 'forge/builder'
|
9
|
+
autoload :Generator, 'forge/generator'
|
10
|
+
autoload :Config, 'forge/config'
|
11
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
require 'sprockets-sass'
|
3
|
+
require 'sass'
|
4
|
+
require 'zip/zip'
|
5
|
+
require 'compass'
|
6
|
+
|
7
|
+
module Forge
|
8
|
+
class Builder
|
9
|
+
def initialize(project)
|
10
|
+
@project = project
|
11
|
+
@task = project.task
|
12
|
+
@templates_path = @project.templates_path
|
13
|
+
@assets_path = @project.assets_path
|
14
|
+
@functions_path = @project.functions_path
|
15
|
+
@includes_path = @project.includes_path
|
16
|
+
|
17
|
+
init_sprockets
|
18
|
+
end
|
19
|
+
|
20
|
+
# Runs all the methods necessary to build a completed project
|
21
|
+
def build
|
22
|
+
clean_build_directory
|
23
|
+
copy_templates
|
24
|
+
copy_functions
|
25
|
+
build_assets
|
26
|
+
end
|
27
|
+
|
28
|
+
# Use the rubyzip library to build a zip from the generated source
|
29
|
+
def zip
|
30
|
+
basename = File.basename(@project.root)
|
31
|
+
|
32
|
+
Zip::ZipFile.open(get_output_filename(basename), Zip::ZipFile::CREATE) do |zip|
|
33
|
+
# Get all filenames in the build directory recursively
|
34
|
+
filenames = Dir[File.join(@project.build_path, '**', '*')]
|
35
|
+
|
36
|
+
# Remove the build directory path from the filename
|
37
|
+
filenames.collect! {|path| path.gsub(/#{@project.build_path}\//, '')}
|
38
|
+
|
39
|
+
# Add each file in the build directory to the zip file
|
40
|
+
filenames.each do |filename|
|
41
|
+
zip.add File.join(basename, filename), File.join(@project.build_path, filename)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Empty out the build directory
|
47
|
+
def clean_build_directory
|
48
|
+
FileUtils.rm_rf Dir.glob(File.join(@project.build_path, '*'))
|
49
|
+
end
|
50
|
+
|
51
|
+
def clean_templates
|
52
|
+
# TODO: cleaner way of removing templates only?
|
53
|
+
Dir.glob(File.join(@project.build_path, '*.php')).each do |path|
|
54
|
+
FileUtils.rm path unless path.include?('functions.php')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def copy_templates
|
59
|
+
template_paths.each do |template_path|
|
60
|
+
FileUtils.cp template_path, @project.build_path unless File.directory?(template_path)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def copy_functions
|
65
|
+
FileUtils.cp_r File.join(@functions_path, 'functions.php'), @project.build_path
|
66
|
+
end
|
67
|
+
|
68
|
+
def clean_includes
|
69
|
+
FileUtils.rm_rf File.join(@project.build_path, 'includes')
|
70
|
+
end
|
71
|
+
|
72
|
+
def copy_includes
|
73
|
+
unless Dir.glob(File.join(@includes_path, '*')).empty?
|
74
|
+
FileUtils.cp_r @includes_path, @project.build_path
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def clean_images
|
79
|
+
FileUtils.rm_rf File.join(@project.build_path, 'images')
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_assets
|
83
|
+
[['style.css'], ['js', 'theme.js']].each do |asset|
|
84
|
+
destination = File.join(@project.build_path, asset)
|
85
|
+
|
86
|
+
sprocket = @sprockets.find_asset(asset.last)
|
87
|
+
|
88
|
+
sprockets_error = false
|
89
|
+
|
90
|
+
# Catch any sprockets errors and continue the process
|
91
|
+
begin
|
92
|
+
FileUtils.mkdir_p(File.dirname(destination)) unless File.directory?(File.dirname(destination))
|
93
|
+
sprocket.write_to(destination) unless sprocket.nil?
|
94
|
+
|
95
|
+
if asset.last == 'style.css' && (not sprockets_error)
|
96
|
+
@task.prepend_file destination, @project.parse_erb(stylesheet_header)
|
97
|
+
end
|
98
|
+
rescue Exception => e
|
99
|
+
@task.say "Error while building #{asset.last}:"
|
100
|
+
@task.say e.message, Thor::Shell::Color::RED
|
101
|
+
File.open(destination, 'w') do |file|
|
102
|
+
file.puts(e.message)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Re-initializing sprockets to prevent further errors
|
106
|
+
# TODO: This is done for lack of a better solution
|
107
|
+
init_sprockets
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Copy the images directory over
|
112
|
+
FileUtils.cp_r File.join(@assets_path, 'images'), File.join(@project.build_path)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def init_sprockets
|
118
|
+
@sprockets = Sprockets::Environment.new
|
119
|
+
|
120
|
+
['javascripts', 'stylesheets'].each do |dir|
|
121
|
+
@sprockets.append_path File.join(@assets_path, dir)
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Add all compass frameworks to the sass path
|
126
|
+
# TODO: This feels really hacky - is there a better way?
|
127
|
+
Compass::Frameworks::ALL.each do |framework|
|
128
|
+
Sass::Engine::DEFAULT_OPTIONS[:load_paths] << framework.stylesheets_directory
|
129
|
+
end
|
130
|
+
|
131
|
+
@sprockets.context_class.instance_eval do
|
132
|
+
def config
|
133
|
+
return {:name => 'asd'}
|
134
|
+
p "CALLING CONFIG"
|
135
|
+
@project.config
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def template_paths
|
141
|
+
Dir.glob(File.join(@templates_path, '**', '*'))
|
142
|
+
end
|
143
|
+
|
144
|
+
# Generate a unique filename for the zip output
|
145
|
+
def get_output_filename(basename)
|
146
|
+
filename = "#{basename}.zip"
|
147
|
+
|
148
|
+
i = 1
|
149
|
+
while File.exists?(filename)
|
150
|
+
filename = "#{basename}(#{i}).zip"
|
151
|
+
i += 1
|
152
|
+
end
|
153
|
+
|
154
|
+
filename
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
def stylesheet_header
|
159
|
+
return @stylesheet_header unless @stylesheet_header.nil?
|
160
|
+
|
161
|
+
file = @task.find_in_source_paths(File.join('config', 'stylesheet_header.erb'))
|
162
|
+
@stylesheet_header = File.expand_path(file)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/lib/forge/cli.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'yaml'
|
3
|
+
require 'guard/forge/assets'
|
4
|
+
require 'guard/forge/config'
|
5
|
+
require 'guard/forge/templates'
|
6
|
+
require 'guard/forge/functions'
|
7
|
+
|
8
|
+
module Forge
|
9
|
+
class CLI < Thor
|
10
|
+
include Thor::Actions
|
11
|
+
|
12
|
+
def self.source_root
|
13
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'layouts'))
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "create DIRECTORY", "Creates a Forge project"
|
17
|
+
method_option :name, :type => :string, :desc => "The theme name"
|
18
|
+
method_option :uri, :type => :string, :desc => "The theme's uri"
|
19
|
+
method_option :author, :type => :string, :desc => "The author of the theme"
|
20
|
+
method_option :author_uri, :type => :string, :desc => "The author's uri"
|
21
|
+
method_option :interactive, :type => :boolean, :desc => "Use interactive configuration setup", :aliases => "-i"
|
22
|
+
def create(dir)
|
23
|
+
prompts = {
|
24
|
+
:name => "What is the name of this theme?",
|
25
|
+
:uri => "What is the website for this theme?",
|
26
|
+
:author => "Who is the author of this theme?",
|
27
|
+
:author_uri => "What is the author's website?"
|
28
|
+
}
|
29
|
+
|
30
|
+
theme = {}
|
31
|
+
|
32
|
+
prompts.each do |k,v|
|
33
|
+
theme[k] = options[k]
|
34
|
+
theme[k] = ask(v) if options[:interactive]
|
35
|
+
end
|
36
|
+
|
37
|
+
theme[:name] = dir if (theme[:name].nil? || theme[:name].empty?)
|
38
|
+
|
39
|
+
project = Forge::Project.create(dir, theme, self)
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "link PATH", "symlink the compiled version of theme to the specified path"
|
43
|
+
long_desc "This command will symlink the compiled version of the theme to the specified path.\n\n"+
|
44
|
+
"To compile the theme use the `forge watch` command"
|
45
|
+
def link(path)
|
46
|
+
project = Forge::Project.new('.', self)
|
47
|
+
|
48
|
+
FileUtils.mkdir_p project.build_path unless File.directory?(project.build_path)
|
49
|
+
|
50
|
+
do_link(project, path)
|
51
|
+
end
|
52
|
+
|
53
|
+
desc "watch", "Start watch process"
|
54
|
+
def watch
|
55
|
+
project = Forge::Project.new('.', self)
|
56
|
+
|
57
|
+
# Empty the build directory before starting up to clean out old files
|
58
|
+
FileUtils.rm_rf project.build_path
|
59
|
+
FileUtils.mkdir_p project.build_path
|
60
|
+
|
61
|
+
Forge::Guard.start(project, self)
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "package", "Compile and zip your project"
|
65
|
+
def package
|
66
|
+
project = Forge::Project.new('.', self)
|
67
|
+
|
68
|
+
builder = Builder.new(project)
|
69
|
+
builder.build
|
70
|
+
builder.zip
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
def do_link(project, path)
|
75
|
+
begin
|
76
|
+
project.link(path)
|
77
|
+
rescue LinkSourceDirNotFound
|
78
|
+
say_status :error, "The path #{File.dirname(path)} does not exist", :red
|
79
|
+
exit 2
|
80
|
+
rescue Errno::EEXIST
|
81
|
+
say_status :error, "The path #{path} already exsts", :red
|
82
|
+
exit 2
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|