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
@@ -1,6 +1,7 @@
1
1
  require "view_component"
2
2
  require "action_cable/engine"
3
3
  require "listen"
4
+ require "yard"
4
5
 
5
6
  module Lookbook
6
7
  class Engine < Rails::Engine
@@ -8,18 +9,17 @@ module Lookbook
8
9
 
9
10
  config.autoload_paths << File.expand_path(root.join("app/components"))
10
11
 
11
- initializer "lookbook.viewcomponent.config" do
12
- Lookbook.config.preview_paths += config.view_component.preview_paths
13
- Lookbook.config.preview_controller ||= config.view_component.preview_controller
14
-
15
- Lookbook.config.components_path = config.view_component.view_component_path if config.view_component.view_component_path.present?
16
-
17
- Lookbook.config.listen_paths += Lookbook.config.preview_paths
18
- Lookbook.config.listen_paths << Lookbook.config.components_path
12
+ config.before_configuration do
13
+ config.lookbook = Lookbook.config
19
14
  end
20
15
 
21
- initializer "lookbook.parser.tags" do
22
- Lookbook::Parser.define_tags(Engine.tags)
16
+ initializer "lookbook.viewcomponent.config_sync" do
17
+ opts.preview_paths += config.view_component.preview_paths
18
+ opts.preview_controller ||= config.view_component.preview_controller
19
+
20
+ if config.view_component.view_component_path.present?
21
+ opts.components_path = config.view_component.view_component_path
22
+ end
23
23
  end
24
24
 
25
25
  initializer "lookbook.assets.serve" do
@@ -29,178 +29,129 @@ module Lookbook
29
29
  )
30
30
  end
31
31
 
32
- config.before_configuration do
33
- config.lookbook = Lookbook.config
32
+ initializer "lookbook.file_watcher.paths" do
33
+ opts.listen_paths += opts.preview_paths
34
+ opts.listen_paths << opts.components_path
34
35
  end
35
36
 
36
- config.after_initialize do
37
- @preview_controller = Lookbook.config.preview_controller.constantize
38
- @preview_controller.include(Lookbook::PreviewController)
39
-
40
- parser.after_parse do |registry|
41
- Preview.load!(registry.all(:class))
42
- reload_ui
43
- end
44
-
45
- if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1.3.1")
46
- # Rails.application.server is only available for newer Rails versions
47
- Rails.application.server do
48
- init_listeners
49
- end
50
- else
51
- # Fallback for older Rails versions - don't start listeners if running in a rake task.
52
- unless prevent_listening?
53
- init_listeners
54
- end
55
- end
56
-
57
- parser.parse do
58
- run_hooks(:after_initialize)
37
+ initializer "lookbook.file_watcher.previews" do
38
+ file_watcher.watch(opts.listen_paths, opts.listen_extensions, wait_for_delay: 0.5) do |changes|
39
+ parser.parse { run_hooks(:after_change, changes) }
59
40
  end
60
41
  end
61
42
 
62
- at_exit do
63
- if listeners.any?
64
- Lookbook.logger.debug "Stopping listeners"
65
- stop_listeners
43
+ initializer "lookbook.file_watcher.pages" do
44
+ file_watcher.watch(opts.page_paths, opts.page_extensions) do |changes|
45
+ self.class.websocket.broadcast(:reload)
46
+ run_hooks(:after_change, changes)
66
47
  end
67
- run_hooks(:before_exit)
68
48
  end
69
49
 
70
- class << self
71
- def init_listeners
72
- config = Lookbook.config
73
- return unless config.listen == true
74
-
75
- listen_paths = PathUtils.normalize_all(config.listen_paths)
76
- if listen_paths.any?
77
- preview_listener = Listen.to(*listen_paths,
78
- only: /\.(#{config.listen_extensions.join("|")})$/,
79
- wait_for_delay: 0.5,
80
- force_polling: config.listen_use_polling) do |modified, added, removed|
81
- parser.parse do
82
- run_hooks(:after_change, {modified: modified, added: added, removed: removed})
83
- end
84
- end
85
- register_listener(preview_listener)
86
- end
87
-
88
- page_paths = PathUtils.normalize_all(config.page_paths)
89
- if page_paths.any?
90
- page_listener = Listen.to(*page_paths,
91
- only: /\.(html.*|md.*)$/,
92
- force_polling: config.listen_use_polling) do |modified, added, removed|
93
- changes = {modified: modified, added: added, removed: removed}
94
- reload_ui
95
- run_hooks(:after_change, changes)
96
- end
97
- register_listener(page_listener)
98
- end
50
+ initializer "lookbook.parser.preview_load_callback" do
51
+ parser.after_parse do |registry|
52
+ Preview.load!(registry.all(:class))
53
+ self.class.websocket.broadcast(:reload)
99
54
  end
55
+ end
100
56
 
101
- def websocket
102
- config = Lookbook.config
103
- return @websocket unless @websocket.nil?
104
- return unless config.auto_refresh == true && config.listen == true && !Rails.env.test?
105
- Lookbook.logger.info "Initializing websocket"
106
-
107
- cable = ActionCable::Server::Configuration.new
108
- cable.cable = {adapter: "async"}.with_indifferent_access
109
- cable.mount_path = nil
110
- cable.connection_class = -> { Lookbook::Connection }
111
- cable.logger = Lookbook.logger
112
-
113
- @websocket ||= if Gem::Version.new(Rails.version) >= Gem::Version.new(6.0)
114
- ActionCable::Server::Base.new(config: cable)
115
- else
116
- ws = ActionCable::Server::Base.new
117
- ws.config = cable
118
- ws
119
- end
120
- end
57
+ # The preview controller handles the rendering of individual previews.
58
+ #
59
+ # Lookbook injects some actions into whichever controller has been
60
+ # specified by the user in order to render previews within the context of
61
+ # the particular controller class instance so that any before_action/after_action
62
+ # callbacks will be correctly processed.
63
+ config.after_initialize do
64
+ @preview_controller = opts.preview_controller.constantize
65
+ @preview_controller.class_eval { include Lookbook::PreviewActions }
66
+ end
121
67
 
122
- def websocket_mount_path
123
- "#{mounted_path}/cable".gsub("//", "/") if websocket?
68
+ config.after_initialize do
69
+ if Rails.application.respond_to?(:server)
70
+ Rails.application.server { file_watcher.start if listen? }
71
+ elsif process.supports_listening?
72
+ file_watcher.start if listen?
124
73
  end
74
+ end
125
75
 
126
- def websocket?
127
- websocket.present?
128
- end
76
+ config.after_initialize do
77
+ parser.parse { run_hooks(:after_initialize) }
78
+ end
129
79
 
130
- def mounted_path
131
- routes.find_script_name({})
132
- end
80
+ def opts
81
+ Lookbook.config
82
+ end
133
83
 
134
- def parser
135
- preview_paths = PathUtils.normalize_all(Lookbook.config.preview_paths)
136
- @parser ||= Lookbook::Parser.new(preview_paths)
84
+ def run_hooks(event_name, *args)
85
+ self.class.hooks.for_event(event_name).each do |hook|
86
+ hook.call(Lookbook, *args)
137
87
  end
88
+ end
138
89
 
139
- def log_level
140
- Lookbook.logger.level
141
- end
90
+ def parser
91
+ @parser ||= PreviewParser.new(opts.preview_paths, Engine.tags)
92
+ end
142
93
 
143
- def app_name
144
- name = if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1")
145
- Rails.application.class.module_parent_name
146
- else
147
- Rails.application.class.parent_name
148
- end
149
- name.underscore
150
- end
94
+ def file_watcher
95
+ @file_watcher ||= FileWatcher.new(force_polling: opts.listen_use_polling)
96
+ end
151
97
 
152
- def register_listener(listener)
153
- listener.start
154
- listeners << listener
155
- end
98
+ def process
99
+ @process ||= Process.new(env: Rails.env)
100
+ end
156
101
 
157
- def listeners
158
- @listeners ||= []
159
- end
102
+ def listen?
103
+ opts.listen && process.supports_listening?
104
+ end
160
105
 
161
- def stop_listeners
162
- listeners.each { |listener| listener.stop }
163
- end
106
+ def self.mount_path
107
+ routes.find_script_name({})
108
+ end
164
109
 
165
- def run_hooks(event_name, *args)
166
- hooks.for_event(event_name).each do |hook|
167
- hook.call(Lookbook, *args)
168
- end
169
- end
110
+ def self.mounted?
111
+ mount_path.present?
112
+ end
170
113
 
171
- def reload_ui
172
- websocket&.broadcast("reload", {})
114
+ def self.app_name
115
+ name = if Rails.application.class.respond_to?(:module_parent_name)
116
+ Rails.application.class.module_parent_name
117
+ else
118
+ Rails.application.class.parent_name
173
119
  end
120
+ name.underscore
121
+ end
174
122
 
175
- def prevent_listening?
176
- Rails.env.test? || running_in_rake_task?
123
+ def self.websocket
124
+ if mounted?
125
+ use_websocket = opts.auto_refresh && opts.listen && process.supports_listening?
126
+ @websocket ||= use_websocket ? Websocket.new(mount_path, logger: Lookbook.logger) : Websocket.noop
127
+ else
128
+ Websocket.noop
177
129
  end
130
+ end
178
131
 
179
- def running_in_rake_task?
180
- if defined?(Rake) && Rake.respond_to?(:application)
181
- File.basename($0) == "rake" || Rake.application.top_level_tasks.any?
182
- else
183
- false
184
- end
185
- end
132
+ def self.panels
133
+ @panels ||= PanelStore.init_from_config
134
+ end
186
135
 
187
- def panels
188
- @panels ||= PanelStore.init_from_config
189
- end
136
+ def self.inputs
137
+ @inputs ||= InputStore.init_from_config
138
+ end
190
139
 
191
- def inputs
192
- @inputs ||= InputStore.init_from_config
193
- end
140
+ def self.tags
141
+ @tags ||= TagStore.init_from_config
142
+ end
194
143
 
195
- def tags
196
- @tags ||= TagStore.init_from_config
197
- end
144
+ def self.hooks
145
+ @hooks ||= HookStore.init_from_config
146
+ end
198
147
 
199
- def hooks
200
- @hooks ||= HookStore.init_from_config
201
- end
148
+ def self.preview_controller
149
+ @preview_controller
150
+ end
202
151
 
203
- attr_reader :preview_controller
152
+ at_exit do
153
+ file_watcher.stop
154
+ run_hooks(:before_exit)
204
155
  end
205
156
  end
206
157
  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_all(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/page.rb CHANGED
@@ -13,7 +13,7 @@ module Lookbook
13
13
  :data
14
14
  ]
15
15
 
16
- attr_reader :errors, :rel_path
16
+ attr_reader :errors, :rel_path, :content, :frontmatter
17
17
  attr_accessor :sections
18
18
 
19
19
  def initialize(path, base_path)
@@ -22,9 +22,12 @@ module Lookbook
22
22
  @options = nil
23
23
  @errors = []
24
24
  @sections = []
25
+ @frontmatter = {}
26
+ @content = ""
25
27
  @page_name = remove_position_prefix(path_name)
26
28
  @rel_path = @pathname.relative_path_from(@base_path)
27
29
  page_path = @rel_path.dirname.to_s == "." ? @page_name : "#{@rel_path.dirname}/#{@page_name}"
30
+ extract_frontmatter(file_contents)
28
31
  super(page_path)
29
32
  end
30
33
 
@@ -60,10 +63,6 @@ module Lookbook
60
63
  options[key]
61
64
  end
62
65
 
63
- def content
64
- @content ||= strip_frontmatter(file_contents).strip
65
- end
66
-
67
66
  def matchers
68
67
  normalize_matchers(label)
69
68
  end
@@ -112,17 +111,6 @@ module Lookbook
112
111
 
113
112
  def options
114
113
  return @options if @options
115
- begin
116
- frontmatter = (get_frontmatter(file_contents) || {}).deep_symbolize_keys
117
- rescue => exception
118
- frontmatter = {}
119
- line_number_match = exception.message.match(/.*line\s(\d+)/)
120
- @errors.push(Lookbook::Error.new(exception, **{
121
- title: "YAML frontmatter parsing error",
122
- file_path: @pathname.to_s,
123
- line_number: line_number_match ? line_number_match[1] : false
124
- }))
125
- end
126
114
  @options = Lookbook.config.page_options.deep_merge(frontmatter).with_indifferent_access
127
115
  @options[:id] = generate_id(@options[:id] || lookup_path)
128
116
  @options[:label] ||= name.titleize
@@ -136,6 +124,17 @@ module Lookbook
136
124
  @options
137
125
  end
138
126
 
127
+ def extract_frontmatter(content)
128
+ @frontmatter, @content = FrontmatterExtractor.call(content)
129
+ rescue => exception
130
+ line_number_match = exception.message.match(/.*line\s(\d+)/)
131
+ @errors.push(Lookbook::Error.new(exception, **{
132
+ title: "YAML frontmatter parsing error",
133
+ file_path: @pathname.to_s,
134
+ line_number: line_number_match ? line_number_match[1] : false
135
+ }))
136
+ end
137
+
139
138
  def path_name
140
139
  @pathname.basename(@pathname.extname).to_s.gsub(/\.(html|md)$/, "")
141
140
  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
@@ -8,7 +8,14 @@ module Lookbook
8
8
  def initialize(preview, code_object)
9
9
  @preview = preview
10
10
  @preview_inspector = SourceInspector.new(code_object, eval_scope: preview_class.new)
11
- super(preview_class_path(@preview.name))
11
+ preview_path = preview_class_path(name)
12
+
13
+ if @preview_inspector.logical_path
14
+ basename = preview_path.split("/").last
15
+ preview_path = "#{@preview_inspector.logical_path}/#{basename}"
16
+ end
17
+
18
+ super(preview_path)
12
19
  end
13
20
 
14
21
  def id
@@ -1,5 +1,9 @@
1
1
  module Lookbook
2
- module PreviewController
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
- Utils.with_optional_action_view_annotations do
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
- Utils.with_optional_action_view_annotations do
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
@@ -1,7 +1,7 @@
1
1
  module Lookbook
2
2
  class PreviewExample < Entity
3
3
  attr_reader :name, :preview
4
- delegate :params, :position, :group, :notes, :hidden?, :source, :tags, :tag, to: :@example_inspector
4
+ delegate :position, :group, :notes, :hidden?, :source, :tags, :tag, to: :@example_inspector
5
5
 
6
6
  def initialize(name, preview, code_object)
7
7
  @name = name
@@ -21,10 +21,6 @@ module Lookbook
21
21
  :group
22
22
  end
23
23
 
24
- def params
25
- examples.map(&:params).flatten.uniq { |param| param[:name] }
26
- end
27
-
28
24
  def display_options
29
25
  merged = {}
30
26
  examples.reverse.map do |example|
@@ -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_all(@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