middleman-sculptor 0.3

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 (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