lookbook 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +47 -14
  4. data/app/components/lookbook/code/component.html.erb +1 -1
  5. data/app/components/lookbook/inspector_panel/component.rb +3 -5
  6. data/app/components/lookbook/nav/item/component.html.erb +1 -1
  7. data/app/components/lookbook/params/editor/component.rb +3 -10
  8. data/app/components/lookbook/params/field/component.html.erb +8 -8
  9. data/app/components/lookbook/params/field/component.rb +21 -72
  10. data/app/controllers/concerns/lookbook/targetable_concern.rb +156 -0
  11. data/app/controllers/concerns/lookbook/with_preview_controller_concern.rb +13 -0
  12. data/app/controllers/lookbook/application_controller.rb +13 -3
  13. data/app/controllers/lookbook/inspector_controller.rb +45 -0
  14. data/app/controllers/lookbook/page_controller.rb +11 -7
  15. data/app/controllers/lookbook/previews_controller.rb +4 -210
  16. data/app/helpers/lookbook/output_helper.rb +5 -5
  17. data/app/views/layouts/lookbook/skeleton.html.erb +3 -3
  18. data/app/views/lookbook/index.html.erb +12 -1
  19. data/app/views/lookbook/{previews → inspector}/inputs/_color.html.erb +0 -0
  20. data/app/views/lookbook/{previews → inspector}/inputs/_range.html.erb +0 -0
  21. data/app/views/lookbook/{previews → inspector}/inputs/_select.html.erb +0 -0
  22. data/app/views/lookbook/{previews → inspector}/inputs/_text.html.erb +0 -0
  23. data/app/views/lookbook/{previews → inspector}/inputs/_textarea.html.erb +0 -0
  24. data/app/views/lookbook/{previews → inspector}/inputs/_toggle.html.erb +3 -3
  25. data/app/views/lookbook/{previews → inspector}/panels/_content.html.erb +0 -0
  26. data/app/views/lookbook/{previews → inspector}/panels/_notes.html.erb +0 -0
  27. data/app/views/lookbook/{previews → inspector}/panels/_output.html.erb +0 -0
  28. data/app/views/lookbook/{previews → inspector}/panels/_params.html.erb +4 -4
  29. data/app/views/lookbook/{previews → inspector}/panels/_preview.html.erb +0 -0
  30. data/app/views/lookbook/{previews → inspector}/panels/_source.html.erb +0 -0
  31. data/app/views/lookbook/{previews → inspector}/show.html.erb +4 -1
  32. data/config/app.yml +8 -1
  33. data/config/inputs.yml +12 -12
  34. data/config/panels.yml +5 -5
  35. data/config/routes.rb +5 -5
  36. data/config/tags.yml +4 -1
  37. data/lib/lookbook/engine.rb +101 -150
  38. data/lib/lookbook/file_watcher.rb +47 -0
  39. data/lib/lookbook/page.rb +15 -16
  40. data/lib/lookbook/param.rb +99 -0
  41. data/lib/lookbook/preview.rb +8 -1
  42. data/lib/lookbook/{preview_controller.rb → preview_actions.rb} +14 -3
  43. data/lib/lookbook/preview_example.rb +1 -1
  44. data/lib/lookbook/preview_group.rb +0 -4
  45. data/lib/lookbook/preview_parser.rb +53 -0
  46. data/lib/lookbook/process.rb +21 -0
  47. data/lib/lookbook/services/code/code_beautifier.rb +21 -0
  48. data/lib/lookbook/services/code/code_highlighter.rb +69 -0
  49. data/lib/lookbook/services/data/parsers/data_parser.rb +22 -0
  50. data/lib/lookbook/services/data/parsers/json_parser.rb +7 -0
  51. data/lib/lookbook/services/data/parsers/yaml_parser.rb +7 -0
  52. data/lib/lookbook/services/data/resolvers/data_resolver.rb +70 -0
  53. data/lib/lookbook/services/data/resolvers/eval_resolver.rb +10 -0
  54. data/lib/lookbook/services/data/resolvers/file_resolver.rb +28 -0
  55. data/lib/lookbook/services/data/resolvers/method_resolver.rb +10 -0
  56. data/lib/lookbook/services/data/resolvers/yaml_resolver.rb +18 -0
  57. data/lib/lookbook/services/markdown_renderer.rb +29 -0
  58. data/lib/lookbook/services/string_value_caster.rb +60 -0
  59. data/lib/lookbook/services/tags/tag_options_parser.rb +62 -0
  60. data/lib/lookbook/services/templates/action_view_annotations_handler.rb +21 -0
  61. data/lib/lookbook/services/templates/action_view_annotations_stripper.rb +15 -0
  62. data/lib/lookbook/services/templates/frontmatter_extractor.rb +28 -0
  63. data/lib/lookbook/services/templates/styles_extractor.rb +38 -0
  64. data/lib/lookbook/services/{search_param_builder.rb → urls/search_param_builder.rb} +1 -1
  65. data/lib/lookbook/services/{search_param_parser.rb → urls/search_param_parser.rb} +1 -1
  66. data/lib/lookbook/source_inspector.rb +26 -45
  67. data/lib/lookbook/stores/config_store.rb +7 -8
  68. data/lib/lookbook/stores/input_store.rb +7 -3
  69. data/lib/lookbook/stores/tag_store.rb +3 -5
  70. data/lib/lookbook/support/null_object.rb +10 -0
  71. data/lib/lookbook/support/service.rb +2 -2
  72. data/lib/lookbook/support/store.rb +1 -1
  73. data/lib/lookbook/support/utils/attribute_utils.rb +6 -1
  74. data/lib/lookbook/support/utils/path_utils.rb +6 -3
  75. data/lib/lookbook/tags/component_tag.rb +7 -0
  76. data/lib/lookbook/tags/custom_tag.rb +59 -0
  77. data/lib/lookbook/tags/display_tag.rb +15 -0
  78. data/lib/lookbook/tags/hidden_tag.rb +13 -0
  79. data/lib/lookbook/tags/id_tag.rb +7 -0
  80. data/lib/lookbook/tags/label_tag.rb +4 -0
  81. data/lib/lookbook/tags/logical_path.rb +4 -0
  82. data/lib/lookbook/tags/param_tag.rb +61 -0
  83. data/lib/lookbook/tags/position_tag.rb +16 -0
  84. data/lib/lookbook/tags/tag_provider.rb +18 -0
  85. data/lib/lookbook/tags/yard_tag.rb +62 -0
  86. data/lib/lookbook/utils.rb +0 -40
  87. data/lib/lookbook/version.rb +1 -1
  88. data/lib/lookbook/websocket.rb +60 -0
  89. data/lib/lookbook.rb +2 -1
  90. data/public/lookbook-assets/css/lookbook.css +30 -77
  91. data/public/lookbook-assets/css/lookbook.css.map +1 -1
  92. data/public/lookbook-assets/js/lookbook.js +7 -2
  93. data/public/lookbook-assets/js/lookbook.js.map +1 -1
  94. metadata +55 -26
  95. data/lib/lookbook/code_formatter.rb +0 -68
  96. data/lib/lookbook/markdown.rb +0 -22
  97. data/lib/lookbook/params.rb +0 -157
  98. data/lib/lookbook/parser.rb +0 -42
  99. data/lib/lookbook/tag.rb +0 -122
  100. data/lib/lookbook/tag_options.rb +0 -111
  101. data/lib/lookbook/tags.rb +0 -17
  102. data/lib/lookbook/template_parser.rb +0 -72
@@ -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,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,7 @@
1
+ module Lookbook
2
+ class JsonParser < DataParser
3
+ def parse(input)
4
+ JSON.parse(input.to_s)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Lookbook
2
+ class YamlParser < DataParser
3
+ def parse(input)
4
+ YAML.safe_load(input.to_s)
5
+ end
6
+ end
7
+ 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
15
+ @file = file
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,10 @@
1
+ module Lookbook
2
+ class EvalResolver < DataResolver
3
+ MATCHER = /(\{\{\s?(.*)\s?\}\})$/
4
+ MATCH_INDEX = 2
5
+
6
+ def resolve(input)
7
+ evaluate(input)
8
+ end
9
+ end
10
+ 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
+ path.start_with?(".") ? File.expand_path(path, base_dir) : Rails.root.join(path)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,10 @@
1
+ module Lookbook
2
+ class MethodResolver < DataResolver
3
+ MATCHER = /(:([a-zA-Z_\d]+))$/
4
+ MATCH_INDEX = 2
5
+
6
+ def resolve(input)
7
+ evaluate(input)
8
+ end
9
+ end
10
+ 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
@@ -0,0 +1,29 @@
1
+ require "redcarpet"
2
+
3
+ module Lookbook
4
+ class MarkdownRenderer < Service
5
+ attr_reader :text, :opts
6
+
7
+ def initialize(text, opts = {})
8
+ @text = text
9
+ @opts = opts.to_h
10
+ end
11
+
12
+ def call
13
+ clean_text = ActionViewAnnotationsStripper.call(text)
14
+ md = Redcarpet::Markdown.new(LookbookMarkdownRenderer, opts)
15
+ md.render(clean_text).html_safe
16
+ end
17
+
18
+ class LookbookMarkdownRenderer < Redcarpet::Render::HTML
19
+ def block_code(code, language = "ruby")
20
+ line_numbers = language.to_s.end_with? "-numbered"
21
+ ApplicationController.render(Lookbook::Code::Component.new(**{
22
+ source: code,
23
+ language: language.to_s.chomp("-numbered"),
24
+ line_numbers: line_numbers
25
+ }), layout: nil)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,60 @@
1
+ require "active_model"
2
+
3
+ module Lookbook
4
+ class StringValueCaster < Service
5
+ def initialize(value, type = "string")
6
+ @value = value.to_s
7
+ @type = type.to_s.downcase
8
+ @cast_method = :"cast_to_#{@type}"
9
+
10
+ unless respond_to?(@cast_method)
11
+ raise ArgumentError.new "'#{@type}' is not a valid value type to cast to."
12
+ end
13
+ end
14
+
15
+ def call
16
+ return @nil if @value.empty?
17
+ public_send(@cast_method)
18
+ rescue => exception
19
+ Lookbook.logger.debug "Failed to parse '#{@value}' into a '#{@type}' [#{exception}]"
20
+ raise exception
21
+ end
22
+
23
+ def cast_to_string
24
+ @value
25
+ end
26
+
27
+ def cast_to_symbol
28
+ @value.delete_prefix(":").to_sym if @value.present?
29
+ end
30
+
31
+ def cast_to_hash
32
+ result = YamlParser.call(@value)
33
+ unless result.is_a?(Hash)
34
+ raise ParserError.new "'#{@value}' is not a YAML Hash"
35
+ end
36
+ result
37
+ end
38
+
39
+ def cast_to_array
40
+ result = YamlParser.call(@value)
41
+ unless result.is_a?(Array)
42
+ raise ParserError.new "'#{@value}' is not a YAML Array"
43
+ end
44
+ result
45
+ end
46
+
47
+ def cast_to_datetime
48
+ DateTime.parse(@value)
49
+ end
50
+
51
+ def active_model_cast
52
+ type_class = "ActiveModel::Type::#{@type.camelize}".constantize
53
+ type_class.new.cast(@value)
54
+ end
55
+
56
+ alias_method :cast_to_boolean, :active_model_cast
57
+ alias_method :cast_to_integer, :active_model_cast
58
+ alias_method :cast_to_float, :active_model_cast
59
+ end
60
+ end
@@ -0,0 +1,62 @@
1
+ module Lookbook
2
+ class TagOptionsParser < Service
3
+ RESOLVERS = [
4
+ FileResolver,
5
+ MethodResolver,
6
+ EvalResolver,
7
+ YamlResolver
8
+ ]
9
+
10
+ def initialize(input, resolver_opts = {})
11
+ @input = input.to_s.strip
12
+ @resolver_opts = resolver_opts
13
+ @fallback = resolver_opts.fetch(:fallback, {})
14
+ end
15
+
16
+ def call
17
+ options_string, remaining_text = parse_input(@input)
18
+ resolved_options = resolver(options_string).call(options_string, **@resolver_opts)
19
+ options = prepare_options(resolved_options)
20
+ [options, remaining_text]
21
+ end
22
+
23
+ protected
24
+
25
+ def resolver(options_string)
26
+ if options_string.present?
27
+ handler = RESOLVERS.find { |r| r.resolveable?(options_string) }
28
+ if handler.nil?
29
+ Lookbook.logger.error "Invalid tag options string '#{options_string}'"
30
+ method(:fallback_resolver)
31
+ else
32
+ handler
33
+ end
34
+ else
35
+ method(:fallback_resolver)
36
+ end
37
+ end
38
+
39
+ def fallback_resolver(*args)
40
+ @fallback
41
+ end
42
+
43
+ def parse_input(input)
44
+ matchers.each_with_object(["", input]) do |matcher, result|
45
+ input.match(matcher) do |match_data|
46
+ result[0] = match_data[1] # options string
47
+ result[1] = input.gsub(matcher, "").strip # any remaining text
48
+ return result
49
+ end
50
+ end
51
+ end
52
+
53
+ def matchers
54
+ RESOLVERS.map { |r| r::MATCHER }
55
+ end
56
+
57
+ def prepare_options(options)
58
+ options = options.is_a?(Array) ? {choices: options} : options
59
+ options.is_a?(Hash) ? Store.new(options) : options
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ module Lookbook
2
+ class ActionViewAnnotationsHandler < Service
3
+ attr_reader :disable_annotations
4
+
5
+ def initialize(disable_annotations: true)
6
+ @disable_annotations = disable_annotations
7
+ end
8
+
9
+ def call
10
+ if ActionView::Base.respond_to?(:annotate_rendered_view_with_filenames) && disable_annotations
11
+ original_value = ActionView::Base.annotate_rendered_view_with_filenames
12
+ ActionView::Base.annotate_rendered_view_with_filenames = false
13
+ res = yield
14
+ ActionView::Base.annotate_rendered_view_with_filenames = original_value
15
+ res
16
+ else
17
+ yield
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module Lookbook
2
+ class ActionViewAnnotationsStripper < Service
3
+ attr_reader :text
4
+
5
+ ANNOTATIONS_REGEX = /<!-- (BEGIN|END) (.*) -->/
6
+
7
+ def initialize(text)
8
+ @text = text.to_s
9
+ end
10
+
11
+ def call
12
+ text.gsub(ANNOTATIONS_REGEX, "")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module Lookbook
2
+ class FrontmatterExtractor < Service
3
+ FRONTMATTER_REGEX = /\A---(.|\n)*?---/
4
+
5
+ attr_reader :content
6
+
7
+ def initialize(content)
8
+ @content = content.to_s
9
+ end
10
+
11
+ def call
12
+ frontmatter = extract_frontmatter(content)
13
+ rest = strip_frontmatter(content)
14
+ [frontmatter, rest]
15
+ end
16
+
17
+ protected
18
+
19
+ def extract_frontmatter(text)
20
+ matches = text.match(FRONTMATTER_REGEX)
21
+ matches ? YAML.safe_load(matches[0]).deep_symbolize_keys : {}
22
+ end
23
+
24
+ def strip_frontmatter(text)
25
+ text.gsub(FRONTMATTER_REGEX, "").strip
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ require "css_parser"
2
+
3
+ module Lookbook
4
+ class StylesExtractor < Service
5
+ STYLE_TAGS_REGEX = /<style(?:\s[^>]*)?>((?:(?!<\/style>).)*)<\/style>/m
6
+
7
+ attr_reader :content
8
+
9
+ def initialize(content)
10
+ @content = content.to_s
11
+ end
12
+
13
+ def call
14
+ styles = extract_styles(content)
15
+ rest = strip_styles(content)
16
+ [styles, rest]
17
+ end
18
+
19
+ protected
20
+
21
+ def extract_styles(text)
22
+ css_parser = ::CssParser::Parser.new
23
+ text.scan(STYLE_TAGS_REGEX).flatten.map(&:strip).each do |css|
24
+ css_parser.load_string!(css.strip)
25
+ end
26
+
27
+ styles = []
28
+ css_parser.each_selector do |selector, declarations, specificity|
29
+ styles << "#{selector} { #{declarations} }"
30
+ end
31
+ styles
32
+ end
33
+
34
+ def strip_styles(text)
35
+ text.gsub(STYLE_TAGS_REGEX, "").strip
36
+ end
37
+ end
38
+ end
@@ -7,7 +7,7 @@ module Lookbook
7
7
  end
8
8
 
9
9
  def call
10
- data.map { "#{_1}:#{_2}" }.join("|")
10
+ data.map { |k, v| "#{k}:#{v}" }.join("|")
11
11
  end
12
12
  end
13
13
  end
@@ -8,7 +8,7 @@ module Lookbook
8
8
 
9
9
  def call
10
10
  pairs_str = param_value.split("|")
11
- pairs = pairs_str.map { [*_1.split(":").map(&:strip)] }
11
+ pairs = pairs_str.map { |pair| [*pair.split(":").map(&:strip)] }
12
12
  pairs.to_h.symbolize_keys
13
13
  end
14
14
  end
@@ -11,85 +11,66 @@ module Lookbook
11
11
  end
12
12
 
13
13
  def hidden?
14
- @hidden ||= if code_object&.tag(:hidden)
15
- code_object.tag(:hidden).text.strip != "false"
16
- else
17
- false
18
- end
14
+ @hidden ||= tag_value(:hidden) || false
19
15
  end
20
16
 
21
17
  def id
22
- @id ||= if code_object&.tag(:id)&.text&.present?
23
- generate_id(code_object&.tag(:id)&.text)
24
- end
18
+ @id ||= tag_value(:id)
25
19
  end
26
20
 
27
21
  def label
28
- @label ||= code_object&.tag(:label)&.text
22
+ @label ||= tag_value(:label)
29
23
  end
30
24
 
31
25
  def notes
32
- @notes ||= if code_object&.docstring
33
- code_object.docstring.to_s.strip
34
- end
26
+ @notes ||= code_object.docstring&.to_s&.strip
35
27
  end
36
28
 
37
29
  def group
38
- @group ||= code_object&.group
30
+ @group ||= code_object.group
39
31
  end
40
32
 
41
33
  def position
42
- @position ||= code_object&.tag(:position)&.text&.to_i || 10000
34
+ @position ||= tag_value(:position) || 10000
43
35
  end
44
36
 
45
37
  def components
46
- @components ||= if code_object&.tags(:component).present?
47
- code_object.tags(:component).map do |component|
48
- component.text.constantize
49
- end
50
- else
51
- []
52
- end
38
+ @components ||= Array(code_object.tags(:component)).map(&:value)
53
39
  end
54
40
 
55
- def display_options
56
- return @display_options unless @display_options.nil?
57
- tags = code_object.tags(:display).to_a
58
- pairs = tags.map { KeyValueTagParser.call(_1.text) }
59
-
60
- # dynamic params set at the entity level are
61
- # not (yet) supported so filter them out.
62
- pairs.select! { |pair| !pair[1].is_a?(Array) && !pair[1].is_a?(Hash) }
63
-
64
- pairs.to_h.symbolize_keys
41
+ def logical_path
42
+ path = tag_value(:logical_path)
43
+ path&.delete_prefix("/")&.delete_suffix("/")
65
44
  end
66
45
 
67
- def parameter_defaults
68
- @param_defaults ||= code_object&.parameters&.map { |str| Params.parse_method_param_str(str) }&.compact&.to_h
69
- end
46
+ def display_options
47
+ return @display_options unless @display_options.nil?
70
48
 
71
- def params
72
- @params ||= {}
73
- @params[:param] ||= code_object&.tags("param")&.map do |param|
74
- Lookbook::Params.build_param(param,
75
- default: parameter_defaults[param.name],
76
- eval_scope: @eval_scope)
49
+ # Dynamic params set at the entity level are
50
+ # not (yet?) supported so filter them out.
51
+ display_tags = Array(code_object.tags(:display)).select do |tag|
52
+ !(tag.value.is_a?(Array) || tag.value.is_a?(Hash))
77
53
  end
54
+
55
+ display_tags.map { |tag| [tag.key.to_sym, tag.value] }.to_h
78
56
  end
79
57
 
80
58
  def methods
81
- @methods ||= code_object&.meths
59
+ @methods ||= code_object.meths
82
60
  end
83
61
 
84
62
  def tags(name = nil)
85
- tag_objects = code_object&.tags(name).presence || []
86
- Lookbook::Tags.process_tags(tag_objects,
87
- eval_scope: @eval_scope,
88
- file: (code_object.files.first[0] if code_object.files.any?))
63
+ code_object.tags(name)
89
64
  end
90
65
 
91
66
  def tag(name = nil)
92
67
  tags(name).first
93
68
  end
69
+
70
+ protected
71
+
72
+ def tag_value(tag_name)
73
+ code_object.tag(tag_name).value if code_object.has_tag?(tag_name)
74
+ end
94
75
  end
95
76
  end