forge 0.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.
Files changed (57) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +24 -0
  5. data/Gemfile.lock +81 -0
  6. data/LICENSE +20 -0
  7. data/README.md +24 -0
  8. data/Rakefile +53 -0
  9. data/VERSION +1 -0
  10. data/bin/forge +12 -0
  11. data/features/create.feature +34 -0
  12. data/features/link.feature +14 -0
  13. data/features/step_definitions/forge_steps.rb +38 -0
  14. data/features/support/env.rb +17 -0
  15. data/forge.gemspec +135 -0
  16. data/layouts/config/config.json.erb +13 -0
  17. data/layouts/config/stylesheet_header.erb +11 -0
  18. data/layouts/default/functions/functions.php.erb +21 -0
  19. data/layouts/default/stylesheets/_reset.scss +5 -0
  20. data/layouts/default/stylesheets/_typography.scss +5 -0
  21. data/layouts/default/stylesheets/style.css.scss.erb +28 -0
  22. data/layouts/default/templates/404.php.erb +10 -0
  23. data/layouts/default/templates/archive.php.erb +11 -0
  24. data/layouts/default/templates/attachment.php.erb +34 -0
  25. data/layouts/default/templates/comments.php +2 -0
  26. data/layouts/default/templates/footer.php +9 -0
  27. data/layouts/default/templates/header.php.erb +30 -0
  28. data/layouts/default/templates/index.php +6 -0
  29. data/layouts/default/templates/page.php +18 -0
  30. data/layouts/default/templates/partials/loop.php.erb +30 -0
  31. data/layouts/default/templates/search.php.erb +14 -0
  32. data/layouts/default/templates/sidebar.php +9 -0
  33. data/layouts/default/templates/single.php.erb +32 -0
  34. data/layouts/lib/forge-settings/README.md +43 -0
  35. data/layouts/lib/forge-settings/classes/settings.php +11 -0
  36. data/layouts/lib/forge-settings/classes/settings/collection.php +190 -0
  37. data/layouts/lib/forge-settings/classes/settings/option.php +125 -0
  38. data/layouts/lib/forge-settings/classes/settings/option/select.php +30 -0
  39. data/layouts/lib/forge-settings/classes/settings/option/text.php +15 -0
  40. data/layouts/lib/forge-settings/classes/settings/section.php +56 -0
  41. data/lib/forge.rb +11 -0
  42. data/lib/forge/builder.rb +165 -0
  43. data/lib/forge/cli.rb +86 -0
  44. data/lib/forge/config.rb +63 -0
  45. data/lib/forge/error.rb +8 -0
  46. data/lib/forge/generator.rb +128 -0
  47. data/lib/forge/guard.rb +57 -0
  48. data/lib/forge/project.rb +91 -0
  49. data/lib/forge/version.rb +3 -0
  50. data/lib/guard/forge/assets.rb +31 -0
  51. data/lib/guard/forge/config.rb +31 -0
  52. data/lib/guard/forge/functions.rb +31 -0
  53. data/lib/guard/forge/templates.rb +28 -0
  54. data/spec/lib/forge/config_spec.rb +79 -0
  55. data/spec/lib/forge/project_spec.rb +34 -0
  56. data/spec/spec_helper.rb +13 -0
  57. 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
+ }
@@ -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
@@ -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