lookbook 1.2.1 → 1.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +47 -14
- data/app/assets/lookbook/css/themes/blue.css +4 -2
- data/app/assets/lookbook/css/themes/green.css +66 -0
- data/app/assets/lookbook/css/themes/indigo.css +4 -2
- data/app/assets/lookbook/css/themes/rose.css +66 -0
- data/app/assets/lookbook/css/themes/zinc.css +4 -2
- data/app/components/lookbook/base_component.rb +2 -2
- data/app/components/lookbook/code/component.css +2 -2
- data/app/components/lookbook/code/component.html.erb +3 -2
- data/app/components/lookbook/code/component.rb +13 -2
- data/app/components/lookbook/code/highlight_github.css +406 -0
- data/app/components/lookbook/header/component.html.erb +1 -1
- data/app/components/lookbook/inspector_panel/component.rb +4 -6
- data/app/components/lookbook/nav/component.rb +8 -15
- data/app/components/lookbook/nav/directory/component.html.erb +28 -0
- data/app/components/lookbook/nav/directory/component.rb +4 -0
- data/app/components/lookbook/nav/{item → entity}/component.html.erb +8 -8
- data/app/components/lookbook/nav/entity/component.rb +49 -0
- data/app/components/lookbook/nav/item/component.css +15 -0
- data/app/components/lookbook/nav/item/component.js +4 -0
- data/app/components/lookbook/nav/item/component.rb +13 -56
- data/app/components/lookbook/params/editor/component.html.erb +2 -2
- data/app/components/lookbook/params/editor/component.rb +3 -10
- data/app/components/lookbook/params/field/component.css +3 -3
- data/app/components/lookbook/params/field/component.html.erb +8 -8
- data/app/components/lookbook/params/field/component.rb +21 -72
- data/app/components/lookbook/split_layout/component.html.erb +1 -1
- data/app/components/lookbook/tabs/component.html.erb +1 -1
- data/app/components/lookbook/tabs/component.js +4 -0
- data/app/components/lookbook/tag_component.rb +1 -1
- data/app/components/lookbook/viewport/component.css +1 -1
- data/app/components/lookbook/viewport/component.html.erb +1 -1
- data/app/components/lookbook/viewport/component.rb +1 -1
- data/app/controllers/concerns/lookbook/targetable_concern.rb +131 -0
- data/app/controllers/concerns/lookbook/with_preview_controller_concern.rb +13 -0
- data/app/controllers/lookbook/application_controller.rb +21 -9
- data/app/controllers/lookbook/inspector_controller.rb +45 -0
- data/app/controllers/lookbook/page_controller.rb +13 -9
- data/app/controllers/lookbook/pages_controller.rb +9 -15
- data/app/controllers/lookbook/previews_controller.rb +4 -210
- data/app/helpers/lookbook/application_helper.rb +2 -2
- data/app/helpers/lookbook/output_helper.rb +5 -5
- data/app/helpers/lookbook/page_helper.rb +7 -4
- data/app/views/layouts/lookbook/application.html.erb +40 -38
- data/app/views/layouts/lookbook/page.html.erb +2 -2
- data/app/views/layouts/lookbook/shell.html.erb +3 -2
- data/app/views/layouts/lookbook/skeleton.html.erb +7 -7
- data/app/views/lookbook/index.html.erb +13 -2
- data/app/views/lookbook/{previews → inspector}/inputs/_color.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/inputs/_range.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/inputs/_select.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/inputs/_text.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/inputs/_textarea.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/inputs/_toggle.html.erb +5 -5
- data/app/views/lookbook/{previews → inspector}/panels/_content.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/panels/_notes.html.erb +2 -2
- data/app/views/lookbook/{previews → inspector}/panels/_output.html.erb +0 -0
- data/app/views/lookbook/inspector/panels/_params.html.erb +15 -0
- data/app/views/lookbook/{previews → inspector}/panels/_preview.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/panels/_source.html.erb +0 -0
- data/app/views/lookbook/{previews → inspector}/show.html.erb +5 -2
- data/config/app.yml +11 -1
- data/config/inputs.yml +12 -12
- data/config/languages.yml +41 -0
- data/config/panels.yml +6 -6
- data/config/routes.rb +5 -5
- data/config/tags.yml +8 -1
- data/lib/lookbook/engine.rb +103 -130
- data/lib/lookbook/entities/collections/component_collection.rb +4 -0
- data/lib/lookbook/entities/collections/concerns/hierarchical_collection.rb +23 -0
- data/lib/lookbook/entities/collections/entity_collection.rb +61 -0
- data/lib/lookbook/entities/collections/page_collection.rb +30 -0
- data/lib/lookbook/entities/collections/preview_collection.rb +41 -0
- data/lib/lookbook/entities/collections/preview_example_collection.rb +4 -0
- data/lib/lookbook/entities/component.rb +31 -0
- data/lib/lookbook/entities/concerns/annotatable.rb +58 -0
- data/lib/lookbook/entities/concerns/inspectable.rb +44 -0
- data/lib/lookbook/entities/concerns/locatable.rb +73 -0
- data/lib/lookbook/entities/concerns/navigable.rb +43 -0
- data/lib/lookbook/entities/entity.rb +53 -0
- data/lib/lookbook/entities/page.rb +80 -0
- data/lib/lookbook/entities/page_section.rb +43 -0
- data/lib/lookbook/entities/preview.rb +87 -0
- data/lib/lookbook/entities/preview_example.rb +100 -0
- data/lib/lookbook/entities/preview_group.rb +48 -0
- data/lib/lookbook/file_watcher.rb +47 -0
- data/lib/lookbook/lang.rb +12 -35
- data/lib/lookbook/param.rb +99 -0
- data/lib/lookbook/{preview_controller.rb → preview_actions.rb} +14 -3
- data/lib/lookbook/preview_parser.rb +53 -0
- data/lib/lookbook/process.rb +21 -0
- data/lib/lookbook/rendered_example.rb +37 -0
- data/lib/lookbook/services/code/code_beautifier.rb +21 -0
- data/lib/lookbook/services/code/code_highlighter.rb +69 -0
- data/lib/lookbook/services/code/code_indenter.rb +14 -0
- data/lib/lookbook/services/data/parsers/data_parser.rb +22 -0
- data/lib/lookbook/services/data/parsers/json_parser.rb +7 -0
- data/lib/lookbook/services/data/parsers/yaml_parser.rb +7 -0
- data/lib/lookbook/services/data/resolvers/data_resolver.rb +70 -0
- data/lib/lookbook/services/data/resolvers/eval_resolver.rb +10 -0
- data/lib/lookbook/services/data/resolvers/file_resolver.rb +28 -0
- data/lib/lookbook/services/data/resolvers/method_resolver.rb +10 -0
- data/lib/lookbook/services/data/resolvers/yaml_resolver.rb +18 -0
- data/lib/lookbook/services/entities/entity_tree_builder.rb +45 -0
- data/lib/lookbook/services/markdown_renderer.rb +29 -0
- data/lib/lookbook/services/position_prefix_parser.rb +16 -0
- data/lib/lookbook/services/string_value_caster.rb +60 -0
- data/lib/lookbook/services/tags/tag_options_parser.rb +62 -0
- data/lib/lookbook/services/templates/action_view_annotations_handler.rb +21 -0
- data/lib/lookbook/services/templates/action_view_annotations_stripper.rb +15 -0
- data/lib/lookbook/services/templates/frontmatter_extractor.rb +28 -0
- data/lib/lookbook/services/templates/styles_extractor.rb +38 -0
- data/lib/lookbook/services/{search_param_builder.rb → urls/search_param_builder.rb} +1 -1
- data/lib/lookbook/services/{search_param_parser.rb → urls/search_param_parser.rb} +1 -1
- data/lib/lookbook/stores/config_store.rb +12 -9
- data/lib/lookbook/stores/input_store.rb +7 -3
- data/lib/lookbook/stores/panel_store.rb +2 -2
- data/lib/lookbook/stores/tag_store.rb +3 -5
- data/lib/lookbook/support/null_object.rb +10 -0
- data/lib/lookbook/support/service.rb +2 -2
- data/lib/lookbook/support/store.rb +2 -35
- data/lib/lookbook/support/tree_node.rb +87 -0
- data/lib/lookbook/support/utils/path_utils.rb +32 -5
- data/lib/lookbook/support/utils/utils.rb +24 -0
- data/lib/lookbook/tags/component_tag.rb +13 -0
- data/lib/lookbook/tags/custom_tag.rb +61 -0
- data/lib/lookbook/tags/display_tag.rb +15 -0
- data/lib/lookbook/tags/hidden_tag.rb +13 -0
- data/lib/lookbook/tags/id_tag.rb +7 -0
- data/lib/lookbook/tags/label_tag.rb +4 -0
- data/lib/lookbook/tags/logical_path_tag.rb +7 -0
- data/lib/lookbook/tags/param_tag.rb +63 -0
- data/lib/lookbook/tags/position_tag.rb +16 -0
- data/lib/lookbook/tags/source_tag.rb +7 -0
- data/lib/lookbook/tags/tag_provider.rb +18 -0
- data/lib/lookbook/tags/yard_tag.rb +90 -0
- data/lib/lookbook/theme.rb +8 -0
- data/lib/lookbook/version.rb +1 -1
- data/lib/lookbook/websocket.rb +60 -0
- data/lib/lookbook.rb +13 -8
- data/public/lookbook-assets/css/lookbook.css +487 -411
- data/public/lookbook-assets/css/lookbook.css.map +1 -1
- data/public/lookbook-assets/css/themes/blue.css +3 -1
- data/public/lookbook-assets/css/themes/blue.css.map +1 -1
- data/public/lookbook-assets/css/themes/green.css +68 -0
- data/public/lookbook-assets/css/themes/green.css.map +1 -0
- data/public/lookbook-assets/css/themes/indigo.css +3 -1
- data/public/lookbook-assets/css/themes/indigo.css.map +1 -1
- data/public/lookbook-assets/css/themes/rose.css +68 -0
- data/public/lookbook-assets/css/themes/rose.css.map +1 -0
- data/public/lookbook-assets/css/themes/zinc.css +3 -1
- data/public/lookbook-assets/css/themes/zinc.css.map +1 -1
- data/public/lookbook-assets/js/embed.js +10 -1
- data/public/lookbook-assets/js/embed.js.map +1 -1
- data/public/lookbook-assets/js/lookbook.js +358 -629
- data/public/lookbook-assets/js/lookbook.js.map +1 -1
- metadata +96 -44
- data/app/components/lookbook/code/highlight_github_light.css +0 -217
- data/app/views/lookbook/previews/panels/_params.html.erb +0 -15
- data/lib/lookbook/code_formatter.rb +0 -68
- data/lib/lookbook/collection.rb +0 -161
- data/lib/lookbook/component.rb +0 -34
- data/lib/lookbook/entity.rb +0 -47
- data/lib/lookbook/markdown.rb +0 -22
- data/lib/lookbook/page.rb +0 -195
- data/lib/lookbook/page_collection.rb +0 -19
- data/lib/lookbook/page_section.rb +0 -29
- data/lib/lookbook/params.rb +0 -157
- data/lib/lookbook/parser.rb +0 -42
- data/lib/lookbook/preview.rb +0 -174
- data/lib/lookbook/preview_collection.rb +0 -23
- data/lib/lookbook/preview_example.rb +0 -93
- data/lib/lookbook/preview_group.rb +0 -62
- data/lib/lookbook/source_inspector.rb +0 -95
- data/lib/lookbook/support/utils/attribute_utils.rb +0 -9
- data/lib/lookbook/tag.rb +0 -122
- data/lib/lookbook/tag_options.rb +0 -111
- data/lib/lookbook/tags.rb +0 -17
- data/lib/lookbook/template_parser.rb +0 -72
- data/lib/lookbook/utils.rb +0 -105
@@ -0,0 +1,48 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class PreviewGroup < Entity
|
3
|
+
include Navigable
|
4
|
+
|
5
|
+
attr_reader :examples, :preview
|
6
|
+
|
7
|
+
def initialize(name, examples, preview)
|
8
|
+
@name = Utils.name(name)
|
9
|
+
@examples = PreviewExampleCollection.new(examples)
|
10
|
+
@preview = preview
|
11
|
+
@lookup_path = "#{parent.lookup_path}/#{@name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def display_options
|
15
|
+
merged = {}
|
16
|
+
examples.to_a.reverse.map do |example|
|
17
|
+
merged.merge!(example.display_options)
|
18
|
+
end
|
19
|
+
merged
|
20
|
+
end
|
21
|
+
|
22
|
+
def components
|
23
|
+
@_components ||= ComponentCollection.new(examples.flat_map(&:components).uniq(&:path))
|
24
|
+
end
|
25
|
+
|
26
|
+
def search_terms
|
27
|
+
[parent.label, label]
|
28
|
+
end
|
29
|
+
|
30
|
+
def tags(tag_name = nil)
|
31
|
+
examples.flat_map { |example| example.tags(tag_name) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def tag(tag_name = nil)
|
35
|
+
tags(tag_name).first
|
36
|
+
end
|
37
|
+
|
38
|
+
def url_path
|
39
|
+
lookbook_inspect_path(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
def type
|
43
|
+
:group
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :parent, :preview
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class FileWatcher
|
3
|
+
attr_reader :listeners, :force_polling
|
4
|
+
|
5
|
+
def initialize(force_polling: false)
|
6
|
+
@force_polling = force_polling
|
7
|
+
@listeners = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def watch(paths, extensions = ".*", opts = nil, &block)
|
11
|
+
paths = PathUtils.normalize_paths(paths)
|
12
|
+
|
13
|
+
if paths.any?
|
14
|
+
opts = opts.to_h
|
15
|
+
opts[:only] = /\.(#{Array(extensions).join("|")})$/
|
16
|
+
|
17
|
+
listeners << init_listener(paths, opts, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
if listeners.any?
|
23
|
+
Lookbook.logger.debug "Starting listeners"
|
24
|
+
listeners.each { |l| l.start }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop
|
29
|
+
if listeners.any?
|
30
|
+
Lookbook.logger.debug "Stopping listeners"
|
31
|
+
listeners.each { |l| l.stop }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def init_listener(paths, opts, &block)
|
38
|
+
Listen.to(
|
39
|
+
*paths,
|
40
|
+
**opts,
|
41
|
+
force_polling: force_polling
|
42
|
+
) do |modified, added, removed|
|
43
|
+
block.call({modified: modified, added: added, removed: removed})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/lookbook/lang.rb
CHANGED
@@ -1,46 +1,23 @@
|
|
1
1
|
module Lookbook
|
2
2
|
module Lang
|
3
3
|
class << self
|
4
|
-
|
5
|
-
|
6
|
-
name: "ruby",
|
7
|
-
ext: ".rb",
|
8
|
-
label: "Ruby",
|
9
|
-
comment: "# %s"
|
10
|
-
},
|
11
|
-
{
|
12
|
-
name: "html",
|
13
|
-
ext: ".html",
|
14
|
-
label: "HTML",
|
15
|
-
comment: "<!-- %s -->"
|
16
|
-
},
|
17
|
-
{
|
18
|
-
name: "erb",
|
19
|
-
ext: ".erb",
|
20
|
-
label: "ERB",
|
21
|
-
comment: "<%%# %s %%>"
|
22
|
-
},
|
23
|
-
{
|
24
|
-
name: "haml",
|
25
|
-
ext: ".haml",
|
26
|
-
label: "Haml",
|
27
|
-
comment: "<!-- %s -->"
|
28
|
-
},
|
29
|
-
{
|
30
|
-
name: "slim",
|
31
|
-
ext: ".slim",
|
32
|
-
label: "Slim",
|
33
|
-
comment: "<!-- %s -->"
|
34
|
-
}
|
35
|
-
]
|
4
|
+
CONFIG_FILE = "config/languages.yml"
|
5
|
+
LANGUAGES = [] # retained for backwards compatability
|
36
6
|
|
37
7
|
def find(name)
|
38
|
-
|
8
|
+
languages.find { |l| l[:name] == name.to_s }
|
39
9
|
end
|
40
10
|
|
41
|
-
def guess(path)
|
11
|
+
def guess(path, fallback_name = nil)
|
42
12
|
ext = File.extname(path)
|
43
|
-
|
13
|
+
lang = languages.find { |l| l[:ext] == ext }
|
14
|
+
lang || (find(fallback_name) if fallback_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def languages
|
20
|
+
@_languages ||= [*ConfigLoader.call(CONFIG_FILE).definitions, *LANGUAGES]
|
44
21
|
end
|
45
22
|
end
|
46
23
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Param
|
3
|
+
attr_reader :name, :options, :value_default, :description
|
4
|
+
|
5
|
+
def initialize(name:, input: nil, description: nil, value_type: nil, value_default: nil, value: nil, options: {})
|
6
|
+
@name = name
|
7
|
+
@input = input
|
8
|
+
@description = description
|
9
|
+
@value_type = value_type
|
10
|
+
@value_default = value_default
|
11
|
+
@value = value
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def label
|
16
|
+
options.label || name.titleize
|
17
|
+
end
|
18
|
+
|
19
|
+
def hint
|
20
|
+
options.hint
|
21
|
+
end
|
22
|
+
|
23
|
+
def input
|
24
|
+
@input || guess_input
|
25
|
+
end
|
26
|
+
|
27
|
+
def value
|
28
|
+
@value || value_default
|
29
|
+
end
|
30
|
+
|
31
|
+
def value_type
|
32
|
+
@value_type || guess_value_type
|
33
|
+
end
|
34
|
+
|
35
|
+
def input_options
|
36
|
+
return @_input_options if @_input_options
|
37
|
+
|
38
|
+
runtime_options = options.except([*methods, :name, :value_default, :description])
|
39
|
+
@_input_options ||= Store.new(input_config.options.merge(runtime_options))
|
40
|
+
end
|
41
|
+
|
42
|
+
def input_partial
|
43
|
+
input_config.partial
|
44
|
+
end
|
45
|
+
|
46
|
+
def cast_value
|
47
|
+
raise ArgumentError.new("Cannot cast param '#{name}' without a value set") if value.nil?
|
48
|
+
|
49
|
+
StringValueCaster.call(value, value_type)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.from_tag(tag, value: nil)
|
53
|
+
new(
|
54
|
+
name: tag.name,
|
55
|
+
input: tag.input || tag.options.input,
|
56
|
+
description: tag.description || tag.options.description,
|
57
|
+
value_type: tag.value_type || tag.options.value_type,
|
58
|
+
value_default: tag.value_default,
|
59
|
+
options: tag.options,
|
60
|
+
value: value
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def input_config
|
67
|
+
config = Lookbook::Engine.inputs.get_input(input)
|
68
|
+
config || raise(LookbookError.new("Unknown input type '#{input}'"))
|
69
|
+
end
|
70
|
+
|
71
|
+
def guess_input
|
72
|
+
if @value_type == "boolean" || (@value_type.blank? && boolean?(value_default))
|
73
|
+
"toggle"
|
74
|
+
else
|
75
|
+
"text"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def guess_value_type
|
80
|
+
if input == "toggle"
|
81
|
+
"boolean"
|
82
|
+
elsif input == "number"
|
83
|
+
"integer"
|
84
|
+
elsif boolean?(value_default)
|
85
|
+
"boolean"
|
86
|
+
elsif value_default.is_a?(Symbol)
|
87
|
+
"symbol"
|
88
|
+
elsif ["date", "datetime-local"].include?(input) || value_default.is_a?(DateTime)
|
89
|
+
"datetime"
|
90
|
+
else
|
91
|
+
"string"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def boolean?(value)
|
96
|
+
value == true || value == false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module Lookbook
|
2
|
-
module
|
2
|
+
module PreviewActions
|
3
|
+
def self.included(klass)
|
4
|
+
klass.helper Lookbook::PreviewHelper
|
5
|
+
end
|
6
|
+
|
3
7
|
def render_example_to_string(preview, example_name)
|
4
8
|
prepend_application_view_paths
|
5
9
|
prepend_preview_examples_view_path
|
@@ -12,7 +16,7 @@ module Lookbook
|
|
12
16
|
opts[:layout] = nil
|
13
17
|
opts[:locals] = locals if locals.present?
|
14
18
|
|
15
|
-
|
19
|
+
with_optional_action_view_annotations do
|
16
20
|
render html: render_to_string(template, **opts)
|
17
21
|
end
|
18
22
|
end
|
@@ -20,7 +24,7 @@ module Lookbook
|
|
20
24
|
def render_in_layout_to_string(template, locals, opts = {})
|
21
25
|
append_view_path Lookbook::Engine.root.join("app/views")
|
22
26
|
|
23
|
-
|
27
|
+
with_optional_action_view_annotations do
|
24
28
|
html = render_to_string(template, locals: locals, **determine_layout(opts[:layout]))
|
25
29
|
if opts[:append_html].present?
|
26
30
|
html += opts[:append_html]
|
@@ -28,5 +32,12 @@ module Lookbook
|
|
28
32
|
render html: html
|
29
33
|
end
|
30
34
|
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def with_optional_action_view_annotations(&block)
|
39
|
+
disable = Lookbook.config.preview_disable_action_view_annotations
|
40
|
+
ActionViewAnnotationsHandler.call(disable_annotations: disable, &block)
|
41
|
+
end
|
31
42
|
end
|
32
43
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "yard"
|
2
|
+
|
3
|
+
module Lookbook
|
4
|
+
class PreviewParser
|
5
|
+
def initialize(paths, tags = nil)
|
6
|
+
@paths = paths
|
7
|
+
@after_parse_callbacks = []
|
8
|
+
@after_parse_once_callbacks = []
|
9
|
+
@parsing = false
|
10
|
+
|
11
|
+
define_tags(tags)
|
12
|
+
YARD::Parser::SourceParser.after_parse_list { run_callbacks }
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(&block)
|
16
|
+
unless @parsing
|
17
|
+
@parsing = true
|
18
|
+
@after_parse_once_callbacks << block if block
|
19
|
+
YARD::Registry.clear
|
20
|
+
YARD.parse(paths)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_parse(&block)
|
25
|
+
@after_parse_callbacks << block
|
26
|
+
end
|
27
|
+
|
28
|
+
def paths
|
29
|
+
PathUtils.normalize_paths(@paths).map { |path| "#{path}/**/*preview.rb" }
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def callbacks
|
35
|
+
[
|
36
|
+
*@after_parse_callbacks,
|
37
|
+
*@after_parse_once_callbacks
|
38
|
+
]
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_callbacks
|
42
|
+
callbacks.each { |cb| cb.call(YARD::Registry) }
|
43
|
+
@after_parse_once_callbacks = []
|
44
|
+
@parsing = false
|
45
|
+
end
|
46
|
+
|
47
|
+
def define_tags(tags = nil)
|
48
|
+
tags.to_h.each do |name, tag|
|
49
|
+
YARD::Tags::Library.define_tag(tag[:label], name, Lookbook::TagProvider)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class Process
|
3
|
+
attr_reader :env
|
4
|
+
|
5
|
+
def initialize(env: Rails.env)
|
6
|
+
@env = env
|
7
|
+
end
|
8
|
+
|
9
|
+
def supports_listening?
|
10
|
+
!rake_task? && !Rails.const_defined?(:Console)
|
11
|
+
end
|
12
|
+
|
13
|
+
def rake_task?
|
14
|
+
if defined?(Rake) && Rake.respond_to?(:application)
|
15
|
+
File.basename($0) == "rake" || Rake.application.top_level_tasks.any?
|
16
|
+
else
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class RenderedExample
|
3
|
+
delegate_missing_to :example
|
4
|
+
|
5
|
+
attr_reader :output, :example
|
6
|
+
|
7
|
+
def initialize(example, output, params)
|
8
|
+
@example = example
|
9
|
+
@params = params
|
10
|
+
@output = output
|
11
|
+
end
|
12
|
+
|
13
|
+
def source
|
14
|
+
has_custom_template? ? template_source(template) : example.source
|
15
|
+
end
|
16
|
+
|
17
|
+
def source_lang
|
18
|
+
has_custom_template? ? template_lang(template) : example.source_lang
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
attr_reader :params
|
24
|
+
|
25
|
+
def render_args
|
26
|
+
@_render_args ||= preview.render_args(example.name, params: params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def template
|
30
|
+
render_args[:template]
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_custom_template?
|
34
|
+
template != "view_components/preview" && !custom_source?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "htmlbeautifier"
|
2
|
+
|
3
|
+
module Lookbook
|
4
|
+
class CodeBeautifier < Service
|
5
|
+
attr_reader :source, :opts
|
6
|
+
|
7
|
+
def initialize(source, opts = {})
|
8
|
+
@source = source.to_s
|
9
|
+
@opts = opts
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
language = opts.fetch(:language, "html")
|
14
|
+
stripped_source = source.strip
|
15
|
+
result = (language.downcase == "html") ? HtmlBeautifier.beautify(stripped_source, opts) : stripped_source
|
16
|
+
result.to_s.strip.html_safe
|
17
|
+
rescue
|
18
|
+
source.strip.html_safe
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "rouge"
|
2
|
+
require "htmlentities"
|
3
|
+
|
4
|
+
module Lookbook
|
5
|
+
class CodeHighlighter < Service
|
6
|
+
attr_reader :source
|
7
|
+
|
8
|
+
def initialize(source, opts = {})
|
9
|
+
@source = source.to_s
|
10
|
+
@opts = opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def opts
|
14
|
+
(@opts.is_a?(String) || @opts.is_a?(Symbol)) ? {language: @opts} : @opts.to_h
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
coder = HTMLEntities.new
|
19
|
+
decoded_source = coder.decode(source)
|
20
|
+
language = opts.fetch(:language, "ruby")
|
21
|
+
formatter = LookbookFormatter.new(language: language, **opts)
|
22
|
+
lexer = Rouge::Lexer.find(language.to_s) || Rouge::Lexer.find("plaintext")
|
23
|
+
formatter.format(lexer.lex(decoded_source)).html_safe
|
24
|
+
end
|
25
|
+
|
26
|
+
class LookbookFormatter < Rouge::Formatters::HTML
|
27
|
+
def initialize(**opts)
|
28
|
+
@opts = opts
|
29
|
+
@highlight_lines = opts[:highlight_lines].to_a || []
|
30
|
+
@start_line = opts[:start_line] || 1
|
31
|
+
@language = opts[:language]
|
32
|
+
end
|
33
|
+
|
34
|
+
def stream(tokens, &block)
|
35
|
+
lines = token_lines(tokens)
|
36
|
+
|
37
|
+
yield "<div class='wrapper'>"
|
38
|
+
|
39
|
+
if @opts[:line_numbers]
|
40
|
+
yield "<div class='line-numbers'>"
|
41
|
+
lines.each.with_index do |line, i|
|
42
|
+
yield "<div class='line #{"highlighted" if highlighted?(i)}'><span class='line-number'>#{line_number(i)}</span></div>"
|
43
|
+
end
|
44
|
+
yield "</div>"
|
45
|
+
end
|
46
|
+
|
47
|
+
yield "<pre class='code highlight' data-lang='#{@language}'><code>"
|
48
|
+
lines.each.with_index do |line_tokens, i|
|
49
|
+
yield "<div class='line#{" highlighted" if highlighted?(i)}'>"
|
50
|
+
line_tokens.each do |token, value|
|
51
|
+
yield span(token, value)
|
52
|
+
end
|
53
|
+
yield "</div>"
|
54
|
+
end
|
55
|
+
yield "</code></pre>"
|
56
|
+
|
57
|
+
yield "</div>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def highlighted?(i)
|
61
|
+
@highlight_lines.include?(i + 1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def line_number(i)
|
65
|
+
@start_line + i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class CodeIndenter < Service
|
3
|
+
def initialize(source)
|
4
|
+
@source = source.to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
def call
|
8
|
+
source = @source.chomp
|
9
|
+
last = source.split(/\r?\n/).last
|
10
|
+
indent = last ? last[/^([ \t]*)/, 1].length : 0
|
11
|
+
source.gsub(/^[ \t]{#{indent}}/, "")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class DataParser < Service
|
3
|
+
def initialize(input, fail_silently: false, fallback: nil)
|
4
|
+
@input = input
|
5
|
+
@fail_silently = fail_silently
|
6
|
+
@fallback = fallback
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
result = @input.present? ? parse(@input) : @fallback
|
11
|
+
result.is_a?(Hash) ? result.deep_symbolize_keys : result
|
12
|
+
rescue => exception
|
13
|
+
@fail_silently ? @fallback : raise(exception)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def parse(input)
|
19
|
+
raise ParserError.new "DataParser must be subclassed with a :parse method defined"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class DataResolver < Service
|
3
|
+
MATCHER = /(?!.*)/
|
4
|
+
MATCH_INDEX = 1
|
5
|
+
|
6
|
+
attr_reader :eval_context, :base_dir, :file, :fallback
|
7
|
+
|
8
|
+
def initialize(input, eval_context: nil, permit_eval: false, fail_silently: false, base_dir: Rails.root, file: nil, fallback: nil)
|
9
|
+
@input = input.to_s
|
10
|
+
@eval_context = eval_context
|
11
|
+
@permit_eval = permit_eval
|
12
|
+
@fail_silently = fail_silently
|
13
|
+
@fallback = fallback
|
14
|
+
@base_dir = base_dir.to_s
|
15
|
+
@file = file.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
resolve extract(@input)
|
20
|
+
rescue => exception
|
21
|
+
Lookbook.logger.debug "Data resolution failed. (Input: '#{@input}')"
|
22
|
+
@fail_silently ? fallback : raise(exception)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.resolveable?(input)
|
26
|
+
input.to_s.match?(self::MATCHER)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def extract(input)
|
32
|
+
match_data = input.match(self.class::MATCHER)
|
33
|
+
if match_data.nil?
|
34
|
+
raise_error "Invalid data '#{input}'"
|
35
|
+
else
|
36
|
+
match_data[self.class::MATCH_INDEX]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def resolve(input)
|
41
|
+
raise ParserError.new "OptionsResolver must be subclassed with a :resolve method defined"
|
42
|
+
end
|
43
|
+
|
44
|
+
def evaluate(input, fallback = @fallback)
|
45
|
+
if evaluatable?
|
46
|
+
begin
|
47
|
+
eval_context.instance_eval(input.to_s)
|
48
|
+
rescue => exception
|
49
|
+
raise_error "Could not evaluate statetment (#{exception.message})", exception
|
50
|
+
end
|
51
|
+
else
|
52
|
+
Lookbook.logger.debug "Data cannot be evaluated (Input: '#{input}')"
|
53
|
+
fallback
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def raise_error(message, original_exception = nil)
|
58
|
+
raise ParserError.new message, original: original_exception, scope: "resolvers"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def evaluatable?
|
64
|
+
if !@permit_eval
|
65
|
+
raise_error "Runtime evaluation is not permitted"
|
66
|
+
end
|
67
|
+
eval_context.present?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class FileResolver < DataResolver
|
3
|
+
MATCHER = /(\S+\.(json|yml))$/
|
4
|
+
MATCH_INDEX = 1
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def resolve(input)
|
9
|
+
path = resolve_path(input, base_dir)
|
10
|
+
content = read_file(path)
|
11
|
+
|
12
|
+
case path.extname
|
13
|
+
when ".json"
|
14
|
+
JsonParser.call(content)
|
15
|
+
when ".yml"
|
16
|
+
YamlParser.call(content)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def read_file(path)
|
21
|
+
File.exist?(path.to_s) ? File.read(path) : raise_error("The data file at '#{path}' could not be found")
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve_path(path, base_dir)
|
25
|
+
Pathname(path.start_with?(".") ? File.expand_path(path, base_dir) : Rails.root.join(path))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Lookbook
|
2
|
+
class YamlResolver < DataResolver
|
3
|
+
MATCHER = /((?:\{|\[)(.*?)(?:\]|\}))$/m
|
4
|
+
MATCH_INDEX = 1
|
5
|
+
|
6
|
+
def self.resolveable?(input)
|
7
|
+
input.to_s.match?(MATCHER) && YamlParser.call(input, fail_silently: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def resolve(input)
|
13
|
+
YamlParser.call(input)
|
14
|
+
rescue Psych::SyntaxError => exception
|
15
|
+
raise_error "YAML parse error (#{exception}) in '#{file}'", exception
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|