middleman-sculptor 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +10 -0
  3. data/.gitignore +9 -0
  4. data/Gemfile +3 -0
  5. data/Rakefile +2 -0
  6. data/bin/middleman-sculptor +7 -0
  7. data/bin/sculpt +7 -0
  8. data/lib/middleman-sculptor.rb +16 -0
  9. data/lib/middleman-sculptor/cli.rb +57 -0
  10. data/lib/middleman-sculptor/cli/init.rb +15 -0
  11. data/lib/middleman-sculptor/cli/version.rb +12 -0
  12. data/lib/middleman-sculptor/extension.rb +13 -0
  13. data/lib/middleman-sculptor/hash_dot_syntax.rb +6 -0
  14. data/lib/middleman-sculptor/helpers/model.rb +110 -0
  15. data/lib/middleman-sculptor/helpers/outliner.rb +44 -0
  16. data/lib/middleman-sculptor/helpers/resources.rb +155 -0
  17. data/lib/middleman-sculptor/resource_patch.rb +49 -0
  18. data/lib/middleman-sculptor/sprockets_patch.rb +48 -0
  19. data/lib/middleman-sculptor/template.rb +39 -0
  20. data/lib/middleman-sculptor/template/.bowerrc +3 -0
  21. data/lib/middleman-sculptor/template/.editorconfig +10 -0
  22. data/lib/middleman-sculptor/template/.gitignore +2 -0
  23. data/lib/middleman-sculptor/template/Gemfile.tt +11 -0
  24. data/lib/middleman-sculptor/template/bower.json +12 -0
  25. data/lib/middleman-sculptor/template/config.tt +149 -0
  26. data/lib/middleman-sculptor/template/data/.gitkeep +0 -0
  27. data/lib/middleman-sculptor/template/source/assets/images/.gitkeep +0 -0
  28. data/lib/middleman-sculptor/template/source/assets/scripts/app.js +6 -0
  29. data/lib/middleman-sculptor/template/source/assets/scripts/glyptotheque.js +8 -0
  30. data/lib/middleman-sculptor/template/source/assets/scripts/glyptotheque/controllers.js +1 -0
  31. data/lib/middleman-sculptor/template/source/assets/scripts/glyptotheque/directives.js +124 -0
  32. data/lib/middleman-sculptor/template/source/assets/scripts/glyptotheque/services.js +1 -0
  33. data/lib/middleman-sculptor/template/source/assets/scripts/head-js.js +1 -0
  34. data/lib/middleman-sculptor/template/source/assets/scripts/iframe-js.js +1 -0
  35. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque.scss +9 -0
  36. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_$variables.scss +3 -0
  37. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_base.scss +58 -0
  38. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_codehilite.scss +10 -0
  39. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_html-outline.scss +131 -0
  40. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_model-utils.scss +78 -0
  41. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_model.scss +48 -0
  42. data/lib/middleman-sculptor/template/source/assets/styles/glyptotheque/_nav.scss +172 -0
  43. data/lib/middleman-sculptor/template/source/assets/styles/main.scss +18 -0
  44. data/lib/middleman-sculptor/template/source/assets/styles/pygments/borland.css +46 -0
  45. data/lib/middleman-sculptor/template/source/assets/styles/pygments/colorful.css +61 -0
  46. data/lib/middleman-sculptor/template/source/assets/styles/pygments/github.css +61 -0
  47. data/lib/middleman-sculptor/template/source/assets/styles/pygments/trac.css +59 -0
  48. data/lib/middleman-sculptor/template/source/glyptotheque/_model-outline-element.slim +22 -0
  49. data/lib/middleman-sculptor/template/source/glyptotheque/_model-outline.slim +3 -0
  50. data/lib/middleman-sculptor/template/source/glyptotheque/_model-utils.slim +13 -0
  51. data/lib/middleman-sculptor/template/source/glyptotheque/_model.slim +26 -0
  52. data/lib/middleman-sculptor/template/source/glyptotheque/_nav.slim +31 -0
  53. data/lib/middleman-sculptor/template/source/glyptotheque/directory-index.html.slim +14 -0
  54. data/lib/middleman-sculptor/template/source/glyptotheque/site-index.html.slim +5 -0
  55. data/lib/middleman-sculptor/template/source/glyptotheque/sitemap.json.erb +1 -0
  56. data/lib/middleman-sculptor/template/source/layouts/glyptotheque.slim +19 -0
  57. data/lib/middleman-sculptor/template/source/layouts/isolated.slim +19 -0
  58. data/lib/middleman-sculptor/template/source/layouts/layout.slim +18 -0
  59. data/lib/middleman-sculptor/version.rb +5 -0
  60. data/lib/middleman_extension.rb +1 -0
  61. data/middleman-sculptor.gemspec +29 -0
  62. metadata +197 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb144c55d87bf80698a9f4f5bc27c10e5c3d0849
4
+ data.tar.gz: 649c58c85fd0a5d5d1abb02162dd5c200b07b81e
5
+ SHA512:
6
+ metadata.gz: d9b30166947a96a53dabb60084378cb7ad5f3dd74d9454cacdf38f122f0db313fec61d8529fc14693a5b7b4bb0839b85942014141aeb03d688e1984d6775f5af
7
+ data.tar.gz: f4bb812b679077796f884183e4113e2b1e6a5f9b29f0753aec5be3f89fa9ab9a5aa8759fbde71664b8fee5102510a886adc2cde53b1be47c8c4a74752da25320
@@ -0,0 +1,10 @@
1
+ # editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ charset = utf-8
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
@@ -0,0 +1,9 @@
1
+ *.gem
2
+ .bundle
3
+ .config
4
+ Gemfile.lock
5
+ pkg/
6
+ tmp
7
+ build/
8
+ .sass-cache
9
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', __FILE__)
4
+
5
+ require 'middleman-sculptor'
6
+
7
+ Middleman::Sculptor::Cli::Base.start
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', __FILE__)
4
+
5
+ require 'middleman-sculptor'
6
+
7
+ Middleman::Sculptor::Cli::Base.start
@@ -0,0 +1,16 @@
1
+ require 'middleman-core'
2
+ require 'middleman-sculptor/hash_dot_syntax'
3
+ require 'middleman-sculptor/cli'
4
+ require 'middleman-sculptor/resource_patch'
5
+ require 'middleman-sculptor/sprockets_patch'
6
+
7
+ begin
8
+ require "middleman-sculptor/template"
9
+ rescue LoadError
10
+ # v4
11
+ end
12
+
13
+ ::Middleman::Extensions.register(:sculptor) do
14
+ require 'middleman-sculptor/extension'
15
+ ::Middleman::SculptorExtension
16
+ end
@@ -0,0 +1,57 @@
1
+ require 'middleman-core/cli'
2
+
3
+ require 'middleman-sculptor/cli/version'
4
+ require 'middleman-sculptor/cli/init'
5
+
6
+ module Middleman::Sculptor::Cli
7
+ class Base < Thor
8
+ @@middleman_classes = []
9
+
10
+ def self.exit_on_failure?
11
+ true
12
+ end
13
+
14
+ def self.add_middleman_task(klass)
15
+ klass.namespace "sculptor_#{klass.namespace}"
16
+ @@middleman_classes << klass
17
+ end
18
+
19
+ add_middleman_task Middleman::Cli::Init
20
+ # add_middleman_task Middleman::Cli::Server
21
+
22
+ def help(meth=nil, subcommand=false)
23
+ if meth && !self.respond_to?(meth)
24
+ klass, task = Thor::Util.find_class_and_task_by_namespace("sculptor_#{meth}:#{meth}")
25
+ klass.start(['-h', task].compact, shell: shell)
26
+ else
27
+ list = []
28
+ sculptor_classes = Thor::Util.thor_classes_in(Middleman::Sculptor::Cli)
29
+ classes = @@middleman_classes + sculptor_classes
30
+ classes.each do |thor_class|
31
+ list += thor_class.printable_tasks(false)
32
+ end
33
+ list.sort! { |a, b| a[0] <=> b[0] }
34
+
35
+ shell.say 'Tasks:'
36
+ shell.print_table(list, indent: 2, truncate: true)
37
+ shell.say
38
+ end
39
+ end
40
+
41
+ # Intercept missing methods and search subtasks for them
42
+ # @param [Symbol] meth
43
+ def method_missing(meth, *args)
44
+ meth = meth.to_s
45
+ meth = self.class.map[meth] if self.class.map.key?(meth)
46
+
47
+ klass, task = Thor::Util.find_class_and_task_by_namespace("sculptor_#{meth}:#{meth}")
48
+
49
+ if klass.nil?
50
+ raise Thor::Error, "Command '#{meth}' not found. Try 'middleman-sculptor help' for a list of commands."
51
+ else
52
+ args.unshift(task) if task
53
+ klass.start(args, shell: shell)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,15 @@
1
+ require 'middleman-core/cli'
2
+
3
+ module Middleman::Cli
4
+ class Init < Thor
5
+ def init(name='.')
6
+ key = :sculptor
7
+ unless ::Middleman::Templates.registered.key?(key)
8
+ raise Thor::Error, "Unknown project template '#{key}'"
9
+ end
10
+
11
+ thor_group = ::Middleman::Templates.registered[key]
12
+ thor_group.new([name], options).invoke_all
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ require 'middleman-sculptor/version'
2
+
3
+ module Middleman::Sculptor::Cli
4
+ class Version < Thor
5
+ namespace :sculptor_version
6
+
7
+ desc 'version', 'Show version'
8
+ def version
9
+ say "Sculptor #{Middleman::Sculptor::VERSION} (Middleman #{Middleman::VERSION})"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ require 'middleman-sculptor/helpers/resources'
2
+ require 'middleman-sculptor/helpers/model'
3
+ require 'middleman-sculptor/helpers/outliner'
4
+
5
+ module Middleman
6
+ class SculptorExtension < Extension
7
+ self.defined_helpers = [
8
+ Middleman::Sculptor::Helpers::Resources,
9
+ Middleman::Sculptor::Helpers::Model,
10
+ Middleman::Sculptor::Helpers::Outliner
11
+ ]
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ # Allow keys dot syntax
2
+ class Hash
3
+ def method_missing(sym, *args)
4
+ fetch(sym) { fetch(sym.to_s) { super } }
5
+ end
6
+ end
@@ -0,0 +1,110 @@
1
+ require 'open-uri'
2
+ # require 'nokogiri'
3
+
4
+ module Middleman::Sculptor
5
+ module Helpers
6
+ module Model
7
+ def model(*options, &block)
8
+ id = options.first.is_a?(String) && options.shift || nil
9
+ options = options.first || {}
10
+ if block_given?
11
+ html = capture_html(&block)
12
+ metadata = options
13
+ end
14
+
15
+ if url = options[:url]
16
+ # Remote URL
17
+ if url.start_with?('http')
18
+ result = grab_remote(url, options[:css], options)
19
+ metadata = options
20
+ html = result.to_html
21
+ # Assume local path
22
+ else
23
+ html = partial(relative_dir(current_page.path, url).to_s)
24
+ end
25
+ end
26
+
27
+ if metadata
28
+ options.reverse_merge!(metadata.symbolize_keys!)
29
+ end
30
+
31
+ options[:html] = html
32
+ options[:id] = id
33
+
34
+ models = {}
35
+ models[id] = {
36
+ iframe: options[:iframe] || false,
37
+ html: html
38
+ }
39
+
40
+ current_resource.add_metadata({ models: models })
41
+
42
+ partial('glyptotheque/model', locals: options)
43
+ end
44
+
45
+ def imodel(location=nil, options={}, &block)
46
+ options[:iframe] = true
47
+ model(location, options, &block)
48
+ end
49
+
50
+ def model_source(type, &block)
51
+ source_code = capture_html(&block)
52
+ partial('glyptotheque/model-source', locals: { source_type: type, source_code: source_code })
53
+ end
54
+
55
+ private
56
+
57
+ def grab_remote(url, css, options)
58
+ puts "Fetching from #{url}"
59
+
60
+ css ||= 'body'
61
+
62
+ selector_index_pair = css.split(/\s+#(\d+)$/)
63
+
64
+ if options[:pretty] == true
65
+ doc = Nokogiri::XML(open(url), &:noblanks)
66
+ else
67
+ doc = Nokogiri::HTML(open(url), &:noblanks)
68
+ end
69
+
70
+ result = doc.css(selector_index_pair[0])
71
+ puts "Matching CSS: `#{selector_index_pair[0]}` (found #{pluralize(result.count, 'element')})"
72
+
73
+ # Select element with specified index e.g. `.selector #1`
74
+ if selector_index_pair[1]
75
+ puts "Select index #{selector_index_pair[1].to_i} from the list"
76
+ result = result[selector_index_pair[1].to_i]
77
+ end
78
+
79
+ @root_url = "#{URI(url).scheme}://#{URI(url).host}/"
80
+
81
+ if result.class == Nokogiri::XML::NodeSet
82
+ result.css('[src]').map do |e|
83
+ e.set_attribute('src', parse_relative_url(url, e.attr('src')))
84
+ end
85
+ elsif result.class == Nokogiri::XML::Element
86
+ result.set_attribute('src', parse_relative_url(url, result.attr('src'))) if result.attr('src')
87
+ end
88
+
89
+ if !result || result.is_a?(Array) && result.empty?
90
+ raise "Selector `#{selector_index_pair[0]}`#{selector_index_pair[1] ? '(index: ' + selector_index_pair[1] + ')': ''} not found at remote location`#{url}`"
91
+ end
92
+
93
+ return result
94
+ end
95
+
96
+ def parse_relative_url(url, path)
97
+ is_root = path.match(/^\/[^\/].*/)
98
+ if is_root
99
+ path.sub(/^\/(.*)/, @root_url + $1)
100
+ elsif path.match(/^\.{1,2}\//)
101
+ relative_dir(url, path).to_s
102
+ elsif path.match(/^\/\//)
103
+ "#{URI(url).scheme}:#{path}"
104
+ else
105
+ path
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,44 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+
4
+ module Middleman::Sculptor
5
+ module Helpers
6
+ module Outliner
7
+ def outline(&block)
8
+ html = capture_html(&block)
9
+ doc = Nokogiri::HTML.fragment(html, encoding='utf-8')
10
+
11
+ elements = parse_elements(doc.children)
12
+
13
+ partial('glyptotheque/model-outline', locals: { elements: elements })
14
+ end
15
+
16
+ private
17
+
18
+ def parse_elements(elements)
19
+ result = []
20
+
21
+ elements.each do |e|
22
+ text = e.xpath('text()').text
23
+
24
+ next unless e.element?
25
+
26
+ class_name = e.attributes['class'] && e.attributes['class'].value
27
+ id = e.attributes['id'] && e.attributes['id'].value
28
+ attributes = e.attributes.reject {|k| k == 'class' || k == 'id' }
29
+
30
+ result << {
31
+ el_name: e.name,
32
+ class_name: class_name,
33
+ id: id,
34
+ attrs: attributes.values.map { |a| { name: a.name, value: a.value } },
35
+ children: parse_elements(e.children),
36
+ text: text
37
+ }
38
+ end
39
+
40
+ return result
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,155 @@
1
+ require 'rest-client'
2
+ require 'oj'
3
+
4
+ module Middleman::Sculptor
5
+ module Helpers
6
+ module Resources
7
+ def include_javascripts(javascripts)
8
+ include_assets(:javascript_include_tag, javascripts)
9
+ end
10
+
11
+ def include_stylesheets(stylesheets)
12
+ include_assets(:stylesheet_link_tag, stylesheets)
13
+ end
14
+
15
+ def resource_file(resource)
16
+ resource.path.split('/').last.gsub(resource.ext, '')
17
+ end
18
+
19
+ def resource_dir(resource)
20
+ resource.url.split('/').last
21
+ end
22
+
23
+ def resources_for(dir, ext: 'html', exclude_indexes: false, sort_by: nil, ignore: nil, allow_hidden: false)
24
+ filtered = sitemap.resources
25
+ .reject {|r| r.url == dir} # Exclude main directory index
26
+ .select {|r| r.url.start_with?(dir)} # Select files in the given dir
27
+
28
+ collect_resources(filtered,
29
+ { ext: ext, exclude_indexes: exclude_indexes, sort_by: sort_by, ignore: ignore, allow_hidden: allow_hidden}
30
+ )
31
+ end
32
+
33
+ def resource_tree(dir, ext: 'html', data_fields: [:title], exclude_indexes: false, sort_by: nil, ignore: nil)
34
+ resources_for(dir, ext: ext, exclude_indexes: exclude_indexes, sort_by: sort_by, ignore: ignore)
35
+ .map { |r| parse_resource(r, {
36
+ ext: ext,
37
+ data_fields: data_fields,
38
+ exclude_indexes: exclude_indexes,
39
+ sort_by: sort_by,
40
+ ignore: ignore
41
+ })
42
+ }
43
+ .reject { |r| r[:parent] != '/' } # Remove non-root directories from the root
44
+ end
45
+
46
+ def local_data(path)
47
+ current_path = current_resource.path
48
+ if current_resource.metadata.page.has_key? :local_url
49
+ current_path = current_resource.metadata.page.local_url
50
+ end
51
+ result = sitemap.find_resource_by_path(relative_dir(current_path, path).to_s)
52
+ raise "#{path} not found" unless result
53
+
54
+ case result.ext
55
+ when '.yaml', '.yml'
56
+ result = YAML.load(result.render)
57
+ when '.json'
58
+ result = Oj.load(result.render)
59
+ end
60
+
61
+ result
62
+ end
63
+
64
+ # Use in layouts, in templates either Frontmatter or this method can be used
65
+ def append_class(block_name, appended_classes='')
66
+ current_page_classes = current_page.data[block_name] || ''
67
+ existing_classes_a = current_page_classes.split(' ')
68
+ appended_classes_a = appended_classes.split(' ')
69
+ classes = existing_classes_a.concat(appended_classes_a).reverse.join(' ')
70
+ content_for(block_name, classes)
71
+ end
72
+
73
+ def get(url, options = {})
74
+ begin
75
+ result = RestClient.get(url, options)
76
+ rescue => e
77
+ raise "GET - #{e.message}: '#{url}'"
78
+ end
79
+
80
+ Oj.load(result)
81
+ end
82
+
83
+ def post(url, params = {}, headers = {})
84
+ begin
85
+ result = RestClient.post(url, params, headers)
86
+ rescue => e
87
+ raise "POST - #{e.message}: '#{url}'"
88
+ end
89
+
90
+ Oj.load(result)
91
+ end
92
+
93
+ private
94
+
95
+ def collect_resources(resources, options)
96
+ resources
97
+ .select {|r| r.ext == ".#{options[:ext]}"} # Select only files with specified extension
98
+ .sort_by {|r| r.url } # Sort by url (default)
99
+ .sort_by {|r| r.data[options[:sort_by]] || -1} # Sort by `sort_by` param
100
+ .reject {|r| # Reject hidden (Front matter)
101
+ r.data.hidden unless options[:allow_hidden]
102
+ }
103
+ .reject {|r| # Exclude all directory indexes
104
+ options[:exclude_indexes] ? r.directory_index? : false
105
+ }
106
+ .reject {|r| ignore ? r.url.match(options[:ignore]) : false } # Ignore URLs matching pattern (if provided)
107
+ .reject {|r| r.path.end_with? ("-isolated#{r.ext}")} # Ignore proxied '-isolated' mode pages
108
+ .reject {|r| r.path.start_with? ("glyptotheque/")} # Ignore Sculptor’s partials
109
+ end
110
+
111
+ def parse_resource(r, options)
112
+ data = {}
113
+ data[:path] = url_for("/#{r.path}")
114
+ data[:url] = r.url
115
+
116
+ # Add parent to top-level pages (not containing `/` in path)
117
+ if /^((?!\/).)*$/.match r.path
118
+ data[:parent] = '/'
119
+ end
120
+
121
+ if r.children.any?
122
+ data[:children] = collect_resources(r.children, options).map { |c| parse_resource(c, options) }
123
+ if r.parent
124
+ data[:parent] = r.parent.url
125
+ end
126
+ end
127
+
128
+
129
+ options[:data_fields].each do |field|
130
+ data[field] = r.data[field]
131
+ if field == :title && !r.data.title
132
+ data[field] = resource_dir(r)
133
+ end
134
+ end
135
+ data
136
+ end
137
+
138
+ def include_assets(asset_tag, assets)
139
+ return unless assets
140
+ assets = assets.split(/,\s*/) if assets.is_a? String
141
+ resource_path = current_page.metadata && current_page.metadata.page[:local_url] || current_page.path
142
+ Array(assets).map { |a|
143
+ path = a.start_with?('http') ? a : relative_dir(resource_path, a)
144
+ "\n" + method(asset_tag).call(path)
145
+ }.join
146
+ end
147
+
148
+ # Constructs path relative to base path (first argument)
149
+ def relative_dir(path, *args)
150
+ relative_path = args ? args.join('/') : ''
151
+ Pathname(path).dirname.join(relative_path)
152
+ end
153
+ end
154
+ end
155
+ end