forge 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,63 @@
1
+ require 'json'
2
+
3
+ module Forge
4
+ # Reads/Writes a configuration file in the user's home directory
5
+ #
6
+ class Config
7
+
8
+ @config
9
+
10
+ attr_accessor :config
11
+
12
+ def initialize()
13
+ @config = {
14
+ :theme => {
15
+ :author => nil,
16
+ :author_url => nil,
17
+ },
18
+ :links => []
19
+ }
20
+ end
21
+
22
+ # Provides access to the config using the Hash square brackets
23
+ def [](var)
24
+ @config[var]
25
+ end
26
+
27
+ # Allows modifying variables through hash square brackets
28
+ def []=(var, value)
29
+ @config[var] = value
30
+ end
31
+
32
+ # Returns the path to the user's configuration file
33
+ def config_file
34
+ @config_file ||= File.expand_path(File.join('~', '.forge', 'config.yml'))
35
+ end
36
+
37
+ # Writes the configuration file
38
+ def write(options={})
39
+ # If we're unit testing then it helps to use a
40
+ # StringIO object instead of a file buffer
41
+ io = options[:io] || File.open(self.config_file, 'w')
42
+
43
+ io.write JSON.generate(@config)
44
+
45
+ io.close
46
+
47
+ self
48
+ end
49
+
50
+ # Loads config declarations in user's home dir
51
+ #
52
+ # If file does not exist then it will be created
53
+ def read
54
+ return write unless File.exists?(self.config_file)
55
+
56
+ data = File.open(self.config_file).read
57
+
58
+ @config = data.empty? ? {} : JSON.parse(data)
59
+
60
+ self
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ module Forge
2
+ class Error < StandardError
3
+ end
4
+
5
+ # Raised when the link source could not be found
6
+ class LinkSourceDirNotFound < Error
7
+ end
8
+ end
@@ -0,0 +1,128 @@
1
+
2
+ module Forge
3
+ class Generator
4
+ class << self
5
+ def run(project, layout='default')
6
+ generator = self.new(project, layout)
7
+ generator.run
8
+ end
9
+ end
10
+
11
+ def initialize(project, layout='default')
12
+ @project = project
13
+ @task = project.task
14
+ @layout = layout
15
+ end
16
+
17
+ def create_structure
18
+ # Create the build directory for Forge output
19
+ @task.empty_directory @project.build_path
20
+
21
+ source_paths = [
22
+ ['assets', 'images'],
23
+ ['assets', 'javascripts'],
24
+ ['assets', 'stylesheets'],
25
+
26
+ ['functions'],
27
+
28
+ ['includes'],
29
+
30
+ ['templates', 'pages'],
31
+ ['templates', 'partials'],
32
+ ]
33
+
34
+ # Build out Forge structure in the source directory
35
+ source_paths.each do |path|
36
+ @task.empty_directory File.join(@project.source_path, path)
37
+ end
38
+
39
+ self
40
+ end
41
+
42
+ def copy_stylesheets
43
+ source = File.expand_path(File.join(self.layout_path, 'stylesheets'))
44
+ target = File.expand_path(File.join(@project.assets_path, 'stylesheets'))
45
+
46
+ render_directory(source, target)
47
+
48
+ self
49
+ end
50
+
51
+ def copy_templates
52
+ source = File.expand_path(File.join(self.layout_path, 'templates'))
53
+ target = File.expand_path(File.join(@project.source_path, 'templates'))
54
+
55
+ render_directory(source, target)
56
+
57
+ self
58
+ end
59
+
60
+ def copy_settings_library
61
+ settings_path = @task.find_in_source_paths(File.join('lib', 'forge-settings', 'classes'))
62
+
63
+ source = File.expand_path(settings_path)
64
+ target = File.expand_path(File.join(@project.includes_path, 'forge-settings', '.'))
65
+
66
+ @task.directory(source, target)
67
+ end
68
+
69
+ def copy_functions
70
+ source = File.expand_path(File.join(self.layout_path, 'functions', 'functions.php.erb'))
71
+ target = File.expand_path(File.join(@project.source_path, 'functions', 'functions.php'))
72
+
73
+ write_template(source, target)
74
+ end
75
+
76
+ def layout_path
77
+ @layout_path ||= File.join(Forge::ROOT, 'layouts', @layout)
78
+ end
79
+
80
+ def run
81
+ write_config
82
+ create_structure
83
+ copy_stylesheets
84
+ copy_templates
85
+ copy_functions
86
+ copy_settings_library
87
+ return self
88
+ end
89
+
90
+ def write_config
91
+ write_template(['config', 'config.json.erb'], @project.config_file)
92
+
93
+ self
94
+ end
95
+
96
+ def write_template(source, target)
97
+ source = File.join(source)
98
+ template = File.expand_path(@task.find_in_source_paths((source)))
99
+ target = File.expand_path(File.join(target))
100
+
101
+ @task.create_file target do
102
+ @project.parse_erb(template)
103
+ end
104
+ end
105
+
106
+ protected
107
+ def render_directory(source, target)
108
+ Dir.glob("#{source}/**/*") do |file|
109
+ unless File.directory?(file)
110
+ source_file = file.gsub(source, '')
111
+ target_file = File.join(target, source_file)
112
+
113
+ if source_file.end_with? ".erb"
114
+ target_file = target_file.slice(0..-5)
115
+
116
+ content = @project.parse_erb(file)
117
+ else
118
+ content = File.open(file).read
119
+ end
120
+
121
+ @task.create_file target_file do
122
+ content
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,57 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Forge
5
+ module Guard
6
+
7
+ class << self
8
+ attr_accessor :project, :task, :builder
9
+ end
10
+
11
+ def self.add_guard(&block)
12
+ @additional_guards ||= []
13
+ @additional_guards << block
14
+ end
15
+
16
+ def self.start(project, task, options={}, livereload={})
17
+ @project = project
18
+ @task = task
19
+ @builder = Builder.new(project)
20
+
21
+ options_hash = ""
22
+ options.each do |k,v|
23
+ options_hash << ", :#{k} => '#{v}'"
24
+ end
25
+
26
+ assets_path = @project.assets_path.gsub(/#{@project.root}\//, '')
27
+ source_path = @project.source_path.gsub(/#{@project.root}\//, '')
28
+ config_file = @project.config_file.gsub(/#{@project.root}\//, '')
29
+
30
+ guardfile_contents = %Q{
31
+ guard 'forgeconfig'#{options_hash} do
32
+ watch("#{config_file}")
33
+ end
34
+ guard 'forgeassets' do
35
+ watch(%r{#{assets_path}/javascripts/*})
36
+ watch(%r{#{assets_path}/stylesheets/*})
37
+ watch(%r{#{assets_path}/images/*})
38
+ end
39
+ guard 'forgetemplates' do
40
+ watch(%r{#{source_path}/templates/*})
41
+ watch(%r{#{source_path}/partials/*})
42
+ end
43
+ guard 'forgefunctions' do
44
+ watch(%r{#{source_path}/functions/*})
45
+ watch(%r{#{source_path}/includes/*})
46
+ end
47
+ }
48
+
49
+ (@additional_guards || []).each do |block|
50
+ result = block.call(options, livereload)
51
+ guardfile_contents << result unless result.nil?
52
+ end
53
+
54
+ ::Guard.start({ :guardfile_contents => guardfile_contents })
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,91 @@
1
+ require 'pathname'
2
+ require 'json'
3
+
4
+ module Forge
5
+ class Project
6
+ class << self
7
+ def create(root, config, task)
8
+ root = File.expand_path(root)
9
+
10
+ project = self.new(root, task, config)
11
+ Generator.run(project)
12
+
13
+ project
14
+ end
15
+ end
16
+
17
+ attr_accessor :root, :config, :task
18
+
19
+ def initialize(root, task, config={})
20
+ @root = File.expand_path(root)
21
+ @config = config || {}
22
+ @task = task
23
+
24
+ self.load_config if @config.empty?
25
+ end
26
+
27
+ def assets_path
28
+ @assets_path ||= File.join(self.source_path, 'assets')
29
+ end
30
+
31
+ def build_path
32
+ File.join(self.root, '.forge', 'build')
33
+ end
34
+
35
+ def source_path
36
+ File.join(self.root, 'source')
37
+ end
38
+
39
+ def package_path
40
+ File.join(self.root, 'package')
41
+ end
42
+
43
+ def templates_path
44
+ File.join(self.source_path, 'templates')
45
+ end
46
+
47
+ def functions_path
48
+ File.join(self.source_path, 'functions')
49
+ end
50
+
51
+ def includes_path
52
+ File.join(self.source_path, 'includes')
53
+ end
54
+
55
+ def config_file
56
+ @config_file ||= File.join(self.root, 'config.json')
57
+ end
58
+
59
+ # Create a symlink from source to the project build dir
60
+ def link(source)
61
+ source = File.expand_path(source)
62
+
63
+ unless File.directory?(File.dirname(source))
64
+ raise Forge::LinkSourceDirNotFound
65
+ end
66
+
67
+ @task.link_file build_path, source
68
+ end
69
+
70
+ def theme_id
71
+ File.basename(self.root).gsub(/\W/, '_')
72
+ end
73
+
74
+ def load_config
75
+ unless File.exists?(self.config_file)
76
+ raise Error, "Could not find the config file, are you sure you're in a
77
+ forge project directory?"
78
+ end
79
+
80
+ self.config = JSON.parse File.open(config_file).read
81
+ end
82
+
83
+ def get_binding
84
+ binding
85
+ end
86
+
87
+ def parse_erb(file)
88
+ ERB.new(::File.binread(file), nil, '-', '@output_buffer').result(binding)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module Forge
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Guard
5
+ class ForgeAssets < ::Guard::Guard
6
+
7
+ def initialize(watchers=[], options={})
8
+ super
9
+ end
10
+
11
+ def start
12
+ UI.info "Building all assets"
13
+ ::Forge::Guard.builder.build_assets
14
+ end
15
+
16
+ # Called on Ctrl-\ signal
17
+ # This method should be principally used for long action like running all specs/tests/...
18
+ def run_all
19
+ UI.info "Rebuilding all assets"
20
+ ::Forge::Guard.builder.clean_images
21
+ ::Forge::Guard.builder.build_assets
22
+ end
23
+
24
+ # Called on file(s) modifications
25
+ def run_on_change(paths)
26
+ UI.info "Assets have changed, rebuilding..."
27
+ ::Forge::Guard.builder.clean_images
28
+ ::Forge::Guard.builder.build_assets
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Guard
5
+ class ForgeConfig < ::Guard::Guard
6
+ def initialize(watchers=[], options={})
7
+ super
8
+ end
9
+
10
+ # Called on Ctrl-Z signal
11
+ # This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
12
+ def reload
13
+ UI.info "Reloading project config"
14
+ ::Forge::Guard.project.load_config
15
+ end
16
+
17
+ # Called on Ctrl-\ signal
18
+ # This method should be principally used for long action like running all specs/tests/...
19
+ def run_all
20
+ UI.info "Reloading project config"
21
+ ::Forge::Guard.project.load_config
22
+ true
23
+ end
24
+
25
+ # Called on file(s) modifications
26
+ def run_on_change(paths)
27
+ UI.info "Project config changed, reloading"
28
+ ::Forge::Guard.project.load_config
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Guard
5
+ class ForgeFunctions < ::Guard::Guard
6
+ def initialize(watchers=[], options={})
7
+ super
8
+ end
9
+
10
+ def start
11
+ UI.info "Copying functions over"
12
+ ::Forge::Guard.builder.copy_functions
13
+ ::Forge::Guard.builder.copy_includes
14
+ end
15
+
16
+ def run_all
17
+ UI.info "Rebuilding all functions"
18
+ ::Forge::Guard.builder.copy_functions
19
+ ::Forge::Guard.builder.clean_includes
20
+ ::Forge::Guard.builder.copy_includes
21
+ end
22
+
23
+ # Called on file(s) modifications
24
+ def run_on_change(paths)
25
+ UI.info "Functions have changed, copying over"
26
+ ::Forge::Guard.builder.copy_functions
27
+ ::Forge::Guard.builder.clean_includes
28
+ ::Forge::Guard.builder.copy_includes
29
+ end
30
+ end
31
+ end