lookbook 1.0.0.beta.4 → 1.0.0.beta.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -254
  3. data/app/assets/lookbook/css/lookbook.css +1 -1
  4. data/app/assets/lookbook/css/themes/blue.css +57 -36
  5. data/app/assets/lookbook/css/themes/indigo.css +57 -36
  6. data/app/assets/lookbook/css/themes/zinc.css +57 -36
  7. data/app/assets/lookbook/css/tooltip.css +5 -5
  8. data/app/components/lookbook/base_component.rb +6 -4
  9. data/app/components/lookbook/button/component.html.erb +2 -2
  10. data/app/components/lookbook/copy_button/component.html.erb +3 -3
  11. data/app/components/lookbook/embed/component.html.erb +33 -33
  12. data/app/components/lookbook/embed/component.rb +8 -0
  13. data/app/components/lookbook/filter/component.html.erb +1 -1
  14. data/app/components/lookbook/header/component.html.erb +3 -3
  15. data/app/components/lookbook/inspector_panel/component.html.erb +4 -0
  16. data/app/components/lookbook/inspector_panel/component.rb +36 -0
  17. data/app/components/lookbook/nav/component.html.erb +4 -8
  18. data/app/components/lookbook/nav/component.rb +5 -7
  19. data/app/components/lookbook/nav/item/component.html.erb +12 -6
  20. data/app/components/lookbook/nav/item/component.rb +9 -2
  21. data/app/components/lookbook/page_tabs/component.html.erb +1 -1
  22. data/app/components/lookbook/params_editor/field/component.rb +2 -1
  23. data/app/components/lookbook/prose/component.html.erb +1 -1
  24. data/app/components/lookbook/split_layout/component.html.erb +1 -1
  25. data/app/components/lookbook/tab_panels/panel/component.html.erb +2 -2
  26. data/app/components/lookbook/tabs/component.html.erb +2 -2
  27. data/app/components/lookbook/tabs/dropdown_tab/component.html.erb +2 -2
  28. data/app/components/lookbook/tabs/tab/component.html.erb +2 -2
  29. data/app/components/lookbook/tag_component.rb +5 -10
  30. data/app/components/lookbook/toolbar/component.html.erb +1 -1
  31. data/app/components/lookbook/viewport/component.css +1 -1
  32. data/app/components/lookbook/viewport/component.html.erb +1 -1
  33. data/app/controllers/lookbook/application_controller.rb +2 -2
  34. data/app/controllers/lookbook/pages_controller.rb +1 -0
  35. data/app/controllers/lookbook/previews_controller.rb +15 -3
  36. data/app/helpers/lookbook/application_helper.rb +9 -6
  37. data/app/helpers/lookbook/preview_helper.rb +2 -3
  38. data/app/views/layouts/lookbook/application.html.erb +76 -60
  39. data/app/views/layouts/lookbook/page.html.erb +36 -36
  40. data/app/views/layouts/lookbook/shell.html.erb +6 -0
  41. data/app/views/layouts/lookbook/skeleton.html.erb +15 -14
  42. data/app/views/lookbook/index.html.erb +24 -11
  43. data/app/views/lookbook/pages/show.html.erb +1 -1
  44. data/app/views/lookbook/previews/panels/_content.html.erb +1 -1
  45. data/app/views/lookbook/previews/panels/_notes.html.erb +2 -2
  46. data/app/views/lookbook/previews/panels/_params.html.erb +1 -1
  47. data/app/views/lookbook/previews/show.html.erb +12 -10
  48. data/lib/lookbook/config.rb +63 -36
  49. data/lib/lookbook/data.rb +11 -0
  50. data/lib/lookbook/engine.rb +68 -60
  51. data/lib/lookbook/hooks.rb +21 -0
  52. data/lib/lookbook/markdown.rb +3 -1
  53. data/lib/lookbook/page.rb +4 -0
  54. data/lib/lookbook/panels.rb +15 -0
  55. data/lib/lookbook/params.rb +12 -0
  56. data/lib/lookbook/parser.rb +4 -1
  57. data/lib/lookbook/preview.rb +36 -6
  58. data/lib/lookbook/preview_example.rb +1 -1
  59. data/lib/lookbook/preview_group.rb +8 -0
  60. data/lib/lookbook/source_inspector.rb +9 -0
  61. data/lib/lookbook/store.rb +8 -0
  62. data/lib/lookbook/tag.rb +108 -0
  63. data/lib/lookbook/tags.rb +22 -0
  64. data/lib/lookbook/theme.rb +36 -7
  65. data/lib/lookbook/version.rb +1 -1
  66. data/lib/lookbook.rb +1 -0
  67. data/lib/tasks/lookbook_tasks.rake +1 -1
  68. data/public/lookbook-assets/css/lookbook.css +314 -162
  69. data/public/lookbook-assets/css/lookbook.css.map +1 -1
  70. data/public/lookbook-assets/css/themes/blue.css +57 -36
  71. data/public/lookbook-assets/css/themes/blue.css.map +1 -1
  72. data/public/lookbook-assets/css/themes/indigo.css +57 -36
  73. data/public/lookbook-assets/css/themes/indigo.css.map +1 -1
  74. data/public/lookbook-assets/css/themes/zinc.css +57 -36
  75. data/public/lookbook-assets/css/themes/zinc.css.map +1 -1
  76. data/public/lookbook-assets/js/embed.js +193 -193
  77. data/public/lookbook-assets/js/lookbook.js +1162 -1319
  78. data/public/lookbook-assets/js/lookbook.js.map +1 -1
  79. metadata +11 -4
@@ -22,12 +22,13 @@ module Lookbook
22
22
 
23
23
  preview_paths: [],
24
24
  preview_display_params: {},
25
- preview_options: {},
26
- preview_srcdoc: false,
27
- sort_examples: true,
25
+ preview_srcdoc: nil,
26
+ preview_tags: {},
27
+ sort_examples: false,
28
28
 
29
29
  listen: Rails.env.development?,
30
30
  listen_paths: [],
31
+ listen_extensions: ["rb", "html.*"],
31
32
  listen_use_polling: false,
32
33
 
33
34
  cable_mount_path: "/lookbook-cable",
@@ -38,6 +39,7 @@ module Lookbook
38
39
 
39
40
  ui_theme: "indigo",
40
41
  ui_theme_overrides: {},
42
+ ui_favicon: true,
41
43
 
42
44
  hooks: {
43
45
  after_initialize: [],
@@ -56,7 +58,8 @@ module Lookbook
56
58
  partial: "lookbook/previews/panels/preview",
57
59
  hotkey: "v",
58
60
  panel_classes: "overflow-hidden",
59
- padded: false
61
+ padded: false,
62
+ system: true
60
63
  },
61
64
  output: {
62
65
  pane: :main,
@@ -64,7 +67,8 @@ module Lookbook
64
67
  partial: "lookbook/previews/panels/output",
65
68
  label: "HTML",
66
69
  hotkey: "h",
67
- padded: false
70
+ padded: false,
71
+ system: true
68
72
  },
69
73
  source: {
70
74
  pane: :drawer,
@@ -73,7 +77,8 @@ module Lookbook
73
77
  label: "Source",
74
78
  hotkey: "s",
75
79
  copy: ->(data) { data.examples.map { |e| e.source }.join("\n") },
76
- padded: false
80
+ padded: false,
81
+ system: true
77
82
  },
78
83
  notes: {
79
84
  pane: :drawer,
@@ -82,7 +87,8 @@ module Lookbook
82
87
  label: "Notes",
83
88
  hotkey: "n",
84
89
  disabled: ->(data) { data.examples.select { |e| e.notes.present? }.none? },
85
- padded: false
90
+ padded: false,
91
+ system: true
86
92
  },
87
93
  params: {
88
94
  pane: :drawer,
@@ -91,25 +97,10 @@ module Lookbook
91
97
  label: "Params",
92
98
  hotkey: "p",
93
99
  disabled: ->(data) { data.preview.params.none? },
94
- padded: false
100
+ padded: false,
101
+ system: true
95
102
  }
96
103
  },
97
-
98
- inspector_panel_defaults: {
99
- id: ->(data) { "inspector-panel-#{data.name}" },
100
- partial: "lookbook/previews/panels/content",
101
- content: nil,
102
- label: ->(data) { data.name.titleize },
103
- pane: :drawer,
104
- position: ->(data) { data.index_position },
105
- hotkey: nil,
106
- disabled: false,
107
- show: true,
108
- copy: nil,
109
- panel_classes: nil,
110
- locals: {},
111
- padded: true
112
- },
113
104
  })
114
105
  end
115
106
 
@@ -121,18 +112,35 @@ module Lookbook
121
112
  absolute_path(@options.components_path)
122
113
  end
123
114
 
115
+ def page_paths=(paths = [])
116
+ @options.page_paths += paths if paths.is_a? Array
117
+ end
118
+
124
119
  def page_paths
125
120
  normalize_paths(@options.page_paths)
126
121
  end
127
122
 
123
+ def preview_paths=(paths = [])
124
+ @options.preview_paths += paths if paths.is_a? Array
125
+ end
126
+
128
127
  def preview_paths
129
128
  normalize_paths(@options.preview_paths)
130
129
  end
131
130
 
131
+ def preview_srcdoc=(enable)
132
+ Lookbook.logger.warn "The `preview_srcdoc` config option is deprecated and will be removed in v2.0"
133
+ end
134
+
132
135
  def listen_paths
133
136
  normalize_paths(@options.listen_paths)
134
137
  end
135
138
 
139
+ def listen_extensions=(extensions = [])
140
+ @options.listen_extensions += extensions if extensions.is_a? Array
141
+ @options.listen_extensions.uniq!
142
+ end
143
+
136
144
  def parser_registry_path
137
145
  absolute_path(@options.parser_registry_path)
138
146
  end
@@ -147,16 +155,13 @@ module Lookbook
147
155
  end
148
156
 
149
157
  def define_inspector_panel(name, opts = {})
150
- @options.inspector_panels[name] = opts
151
- if opts[:position].present?
152
- pane = inspector_panels[name].pane.presence || :drawer
153
- siblings = inspector_panels.select do |key, panel|
154
- panel.pane == pane && key != name.to_sym
155
- end
156
- siblings.each do |key, panel|
157
- if panel.position >= opts[:position]
158
- panel.position += 1
159
- end
158
+ pane = opts[:pane].presence || :drawer
159
+ siblings = inspector_panels.select { |key, panel| panel.pane == pane }
160
+ opts[:position] ||= siblings.size + 1
161
+ @options.inspector_panels[name] = opts
162
+ siblings.each do |key, panel|
163
+ if panel.position >= opts[:position]
164
+ panel.position += 1
160
165
  end
161
166
  end
162
167
  end
@@ -173,6 +178,25 @@ module Lookbook
173
178
  amend_inspector_panel(name, false)
174
179
  end
175
180
 
181
+ def inspector_panel_defaults
182
+ {
183
+ id: ->(data) { "inspector-panel-#{data.name}" },
184
+ partial: "lookbook/previews/panels/content",
185
+ content: nil,
186
+ label: ->(data) { data.name.titleize },
187
+ pane: :drawer,
188
+ position: ->(data) { data.index_position },
189
+ hotkey: nil,
190
+ disabled: false,
191
+ show: true,
192
+ copy: nil,
193
+ panel_classes: nil,
194
+ locals: {},
195
+ padded: true,
196
+ system: false
197
+ }
198
+ end
199
+
176
200
  def ui_theme=(name)
177
201
  name = name.to_s
178
202
  if Theme.valid_theme?(name)
@@ -218,8 +242,11 @@ module Lookbook
218
242
  protected
219
243
 
220
244
  def normalize_paths(paths)
221
- paths.map! { |path| absolute_path(path) }
222
- paths.select { |path| Dir.exist?(path) }
245
+ paths.map! do |path|
246
+ full_path = absolute_path(path)
247
+ full_path if Dir.exist?(full_path)
248
+ end.compact!
249
+ paths
223
250
  end
224
251
 
225
252
  def absolute_path(path)
@@ -0,0 +1,11 @@
1
+ module Lookbook
2
+ module Data
3
+ def data
4
+ @data ||= Store.new
5
+ end
6
+
7
+ def data=(props)
8
+ @data = Store.new(props)
9
+ end
10
+ end
11
+ end
@@ -2,12 +2,22 @@ require "rails"
2
2
  require "view_component"
3
3
  require "action_cable/engine"
4
4
  require "listen"
5
+ require "rake"
5
6
 
6
7
  module Lookbook
7
8
 
8
9
  autoload :Config, "lookbook/config"
9
-
10
+ autoload :Data, "lookbook/data"
11
+ autoload :Hooks, "lookbook/hooks"
12
+ autoload :Panels, "lookbook/panels"
13
+ autoload :Tags, "lookbook/tags"
14
+
10
15
  class << self
16
+ include Lookbook::Data
17
+ include Lookbook::Hooks
18
+ include Lookbook::Panels
19
+ include Lookbook::Tags
20
+
11
21
  def version
12
22
  Lookbook::VERSION
13
23
  end
@@ -36,42 +46,24 @@ module Lookbook
36
46
  Preview.all
37
47
  end
38
48
 
39
- def pages
40
- Page.all
41
- end
42
-
43
- def after_initialize(&block)
44
- add_hook(:after_initialize, block)
45
- end
46
-
47
- def before_exit(&block)
48
- add_hook(:before_exit, block)
49
- end
50
-
51
- def after_change(&block)
52
- add_hook(:after_change, block)
53
- end
54
-
55
- def define_panel(name, opts = {})
56
- config.define_inspector_panel(name, opts)
49
+ def previews?
50
+ Preview.any?
57
51
  end
58
52
 
59
- def amend_panel(name, opts = {})
60
- config.amend_inspector_panel(name, opts)
53
+ def pages
54
+ Page.all
61
55
  end
62
56
 
63
- def remove_panel(name)
64
- config.remove_inspector_panel(name)
57
+ def pages?
58
+ Page.any?
65
59
  end
66
60
 
67
61
  def broadcast(event_name, data = {})
68
62
  Engine.websocket&.broadcast(event_name.to_s, data)
69
63
  end
70
64
 
71
- protected
72
-
73
- def add_hook(event_name, block)
74
- config.hooks[event_name] << block
65
+ def theme
66
+ @theme ||= Lookbook::Theme.new(config.ui_theme, config.ui_theme_overrides)
75
67
  end
76
68
  end
77
69
 
@@ -96,7 +88,7 @@ module Lookbook
96
88
  end
97
89
 
98
90
  initializer "lookbook.parser.tags" do
99
- Lookbook::Parser.define_tags
91
+ Lookbook::Parser.define_tags(Lookbook.config.preview_tags)
100
92
  end
101
93
 
102
94
  initializer "lookbook.assets.serve" do
@@ -110,38 +102,20 @@ module Lookbook
110
102
  @preview_controller = Lookbook.config.preview_controller.constantize
111
103
  @preview_controller.include(Lookbook::PreviewController)
112
104
 
113
- if config.lookbook.listen
114
- Listen.logger = Lookbook.logger
115
- preview_listener = Listen.to(
116
- *config.lookbook.listen_paths,
117
- only: /\.(rb|html.*)$/,
118
- force_polling: Lookbook.config.listen_use_polling
119
- ) do |modified, added, removed|
120
- changes = { modified: modified, added: added, removed: removed }
121
- begin
122
- parser.parse
123
- rescue
124
- end
125
- Lookbook::Preview.clear_cache
126
- Lookbook::Engine.reload_ui(changes)
127
- Lookbook::Engine.run_hooks(:after_change, changes)
105
+ if Gem::Version.new(Rails.version) >= Gem::Version.new("6.1.3.1")
106
+ # Rails.application.server is only available for newer Rails versions
107
+ Rails.application.server do
108
+ init_listeners
128
109
  end
129
- Lookbook::Engine.register_listener(preview_listener)
130
-
131
- page_listener = Listen.to(
132
- *config.lookbook.page_paths,
133
- only: /\.(html.*|md.*)$/,
134
- force_polling: Lookbook.config.listen_use_polling
135
- ) do |modified, added, removed|
136
- changes = { modified: modified, added: added, removed: removed }
137
- Lookbook::Engine.reload_ui(changes)
138
- Lookbook::Engine.run_hooks(:after_change, changes)
110
+ else
111
+ # Fallback for older Rails versions - don't start listeners if running in a rake task.
112
+ unless File.basename($0) == "rake" || Rake.application.top_level_tasks.any?
113
+ init_listeners
139
114
  end
140
- Lookbook::Engine.register_listener(page_listener)
141
115
  end
142
116
 
143
117
  if config.lookbook.runtime_parsing
144
- parser.parse
118
+ Lookbook::Engine.parser.parse
145
119
  else
146
120
  unless File.exist?(config.lookbook.parser_registry_path)
147
121
  Lookbook.logger.warn "
@@ -155,8 +129,41 @@ module Lookbook
155
129
  Lookbook::Engine.run_hooks(:after_initialize)
156
130
  end
157
131
 
132
+ def init_listeners
133
+ return unless config.lookbook.listen == true
134
+ Listen.logger = Lookbook.logger
135
+ Lookbook.logger.info "Initializing listeners"
136
+
137
+ preview_listener = Listen.to(
138
+ *config.lookbook.listen_paths,
139
+ only: /\.(#{config.lookbook.listen_extensions.join("|")})$/,
140
+ force_polling: config.lookbook.listen_use_polling
141
+ ) do |modified, added, removed|
142
+ changes = { modified: modified, added: added, removed: removed }
143
+ begin
144
+ Lookbook::Engine.parser.parse
145
+ rescue
146
+ end
147
+ Lookbook::Preview.clear_cache
148
+ Lookbook::Engine.reload_ui(changes)
149
+ Lookbook::Engine.run_hooks(:after_change, changes)
150
+ end
151
+ Lookbook::Engine.register_listener(preview_listener)
152
+
153
+ page_listener = Listen.to(
154
+ *config.lookbook.page_paths,
155
+ only: /\.(html.*|md.*)$/,
156
+ force_polling: config.lookbook.listen_use_polling
157
+ ) do |modified, added, removed|
158
+ changes = { modified: modified, added: added, removed: removed }
159
+ Lookbook::Engine.reload_ui(changes)
160
+ Lookbook::Engine.run_hooks(:after_change, changes)
161
+ end
162
+ Lookbook::Engine.register_listener(page_listener)
163
+ end
164
+
158
165
  at_exit do
159
- if config.lookbook.listen
166
+ if Lookbook::Engine.listeners.any?
160
167
  Lookbook.logger.debug "Stopping listeners"
161
168
  Lookbook::Engine.listeners.each { |listener| listener.stop }
162
169
  end
@@ -166,6 +173,7 @@ module Lookbook
166
173
  class << self
167
174
 
168
175
  def websocket
176
+ return @websocket unless @websocket.nil?
169
177
  if config.lookbook.auto_refresh
170
178
  cable = ActionCable::Server::Configuration.new
171
179
  cable.cable = {adapter: "async"}.with_indifferent_access
@@ -176,9 +184,9 @@ module Lookbook
176
184
  @websocket ||= if Rails.version.to_f >= 6.0
177
185
  ActionCable::Server::Base.new(config: cable)
178
186
  else
179
- websocket ||= ActionCable::Server::Base.new
180
- websocket.config = cable
181
- websocket
187
+ ws = ActionCable::Server::Base.new
188
+ ws.config = cable
189
+ ws
182
190
  end
183
191
  end
184
192
  end
@@ -213,7 +221,7 @@ module Lookbook
213
221
  end
214
222
 
215
223
  def run_hooks(event_name, *args)
216
- Lookbook.config.hooks[event_name].each do |hook|
224
+ config.lookbook.hooks[event_name].each do |hook|
217
225
  hook.call(Lookbook, *args)
218
226
  end
219
227
  end
@@ -0,0 +1,21 @@
1
+ module Lookbook
2
+ module Hooks
3
+ def after_initialize(&block)
4
+ add_hook(:after_initialize, block)
5
+ end
6
+
7
+ def before_exit(&block)
8
+ add_hook(:before_exit, block)
9
+ end
10
+
11
+ def after_change(&block)
12
+ add_hook(:after_change, block)
13
+ end
14
+
15
+ protected
16
+
17
+ def add_hook(event_name, block)
18
+ Lookbook.config.hooks[event_name] << block
19
+ end
20
+ end
21
+ end
@@ -8,10 +8,12 @@ module Lookbook
8
8
  disable_indented_code_blocks: true,
9
9
  strikethrough: true,
10
10
  highlight: true,
11
- with_toc_data: true
11
+ with_toc_data: true,
12
+ lax_spacing: true
12
13
  }
13
14
 
14
15
  def self.render(text)
16
+ text&.gsub!(/\<\!\-\- (BEGIN|END) (.*) \-\-\>/, "")
15
17
  markdown = Redcarpet::Markdown.new(Renderer, Lookbook.config.markdown_options)
16
18
  markdown.render(text).html_safe
17
19
  end
data/lib/lookbook/page.rb CHANGED
@@ -154,6 +154,10 @@ module Lookbook
154
154
  !!find(path)
155
155
  end
156
156
 
157
+ def any?
158
+ all.any?
159
+ end
160
+
157
161
  def all
158
162
  pages, sections =
159
163
  Array(page_paths).flat_map do |dir|
@@ -0,0 +1,15 @@
1
+ module Lookbook
2
+ module Panels
3
+ def define_panel(name, opts = {})
4
+ Lookbook.config.define_inspector_panel(name, opts)
5
+ end
6
+
7
+ def amend_panel(name, opts = {})
8
+ Lookbook.amend_inspector_panel(name, opts)
9
+ end
10
+
11
+ def remove_panel(name)
12
+ Lookbook.remove_inspector_panel(name)
13
+ end
14
+ end
15
+ end
@@ -63,6 +63,14 @@ module Lookbook
63
63
  result = []
64
64
  end
65
65
  result
66
+ when "datetime"
67
+ begin
68
+ result = DateTime.parse(value)
69
+ rescue Date::Error
70
+ Lookbook.logger.debug "Failed to parse '#{value}' into a DateTime"
71
+ result = DateTime.now
72
+ end
73
+ result
66
74
  else
67
75
  begin
68
76
  type_class = "ActiveModel::Type::#{type}".constantize
@@ -92,6 +100,8 @@ module Lookbook
92
100
  "Boolean"
93
101
  elsif default.is_a? Symbol
94
102
  "Symbol"
103
+ elsif ["date", "datetime-local"].include?(input&.downcase) || default.is_a?(DateTime)
104
+ "DateTime"
95
105
  else
96
106
  "String"
97
107
  end
@@ -99,6 +109,8 @@ module Lookbook
99
109
 
100
110
  def input_text?(input)
101
111
  [
112
+ "date",
113
+ "datetime-local",
102
114
  "email",
103
115
  "number",
104
116
  "tel",
@@ -24,13 +24,16 @@ module Lookbook
24
24
  end
25
25
 
26
26
  class << self
27
- def define_tags
27
+ def define_tags(custom = {})
28
28
  YARD::Tags::Library.define_tag("Hidden status", :hidden)
29
29
  YARD::Tags::Library.define_tag("Label", :label)
30
30
  YARD::Tags::Library.define_tag("Display", :display)
31
31
  YARD::Tags::Library.define_tag("Position", :position)
32
32
  YARD::Tags::Library.define_tag("ID", :id)
33
33
  YARD::Tags::Library.define_tag("Component", :component)
34
+ custom.each do |name, opts|
35
+ YARD::Tags::Library.define_tag(name.to_s.titleize, name)
36
+ end
34
37
  end
35
38
  end
36
39
  end
@@ -3,7 +3,7 @@ module Lookbook
3
3
  include Utils
4
4
 
5
5
  delegate :name, :render_args, to: :@preview
6
- delegate :position, :group, :notes, :hidden?, to: :@preview_inspector
6
+ delegate :position, :group, :notes, :hidden?, :tags, :tag, to: :@preview_inspector
7
7
 
8
8
  def initialize(preview)
9
9
  @preview = preview
@@ -122,14 +122,15 @@ module Lookbook
122
122
  !!find(path)
123
123
  end
124
124
 
125
- def clear_cache
126
- @previews = nil
125
+ def any?
126
+ all.any?
127
127
  end
128
128
 
129
129
  def all
130
130
  load_previews if preview_files.size > ViewComponent::Preview.descendants.size
131
131
 
132
- return @previews if @previews.present?
132
+ @previews = nil if cache_stale?
133
+ return @previews unless @previews.nil?
133
134
 
134
135
  previews = ViewComponent::Preview.descendants.map do |p|
135
136
  new(p)
@@ -144,15 +145,44 @@ module Lookbook
144
145
  end
145
146
 
146
147
  sorted_previews = previews.compact.sort_by { |preview| [preview.position, preview.label] }
147
- @previews ||= PreviewCollection.new(sorted_previews)
148
+ @previews = PreviewCollection.new(sorted_previews)
149
+ mark_as_cached if Lookbook.config.listen == true
150
+ @previews
148
151
  end
149
152
 
150
153
  def errors
151
154
  @errors ||= []
152
155
  end
153
156
 
157
+ def clear_cache
158
+ cache_dir = File.dirname(cache_marker_path)
159
+ FileUtils.mkdir_p(cache_dir) unless File.exists?(cache_dir)
160
+ File.write(cache_marker_path, Time.now.to_i)
161
+ end
162
+
154
163
  protected
155
164
 
165
+ def cache_marker_path
166
+ Rails.root.join("tmp/cache/lookbook-previews")
167
+ end
168
+
169
+ def cache_stale?
170
+ return false if !File.exists?(cache_marker_path)
171
+ cache_timestamp = File.read(cache_marker_path).to_i
172
+ if @last_cache_timestamp.nil? || cache_timestamp > @last_cache_timestamp
173
+ @last_cache_timestamp = cache_timestamp
174
+ true
175
+ else
176
+ false
177
+ end
178
+ end
179
+
180
+ def mark_as_cached
181
+ cache_dir = File.dirname(cache_marker_path)
182
+ FileUtils.mkdir_p(cache_dir) unless File.exists?(cache_dir)
183
+ File.write(cache_marker_path, Time.now)
184
+ end
185
+
156
186
  def load_previews
157
187
  @errors = []
158
188
  preview_files.each do |file|
@@ -166,7 +196,7 @@ module Lookbook
166
196
  )
167
197
  end
168
198
  end
169
-
199
+
170
200
  def preview_files
171
201
  files = Array(Lookbook.config.preview_paths).map do |preview_path|
172
202
  Dir["#{preview_path}/**/*preview.rb"].map do |path|
@@ -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, to: :@example_inspector
4
+ delegate :params, :position, :group, :notes, :hidden?, :source, :tags, :tag, to: :@example_inspector
5
5
 
6
6
  def initialize(name, preview)
7
7
  @name = name
@@ -49,6 +49,14 @@ module Lookbook
49
49
  @preview.hierarchy_depth + 1
50
50
  end
51
51
 
52
+ def tags(name = nil)
53
+ examples.map { |example| example.tags(name) }.flatten
54
+ end
55
+
56
+ def tag(name = nil)
57
+ tags(name).first
58
+ end
59
+
52
60
  alias_method :lookup_path, :path
53
61
  end
54
62
  end
@@ -79,5 +79,14 @@ module Lookbook
79
79
  def methods
80
80
  code_object&.meths
81
81
  end
82
+
83
+ def tags(name = nil)
84
+ tag_objects = code_object&.tags(name).presence || []
85
+ Lookbook::Tags.process_tags(tag_objects)
86
+ end
87
+
88
+ def tag(name = nil)
89
+ tags(name).first
90
+ end
82
91
  end
83
92
  end
@@ -21,6 +21,14 @@ module Lookbook
21
21
  self
22
22
  end
23
23
 
24
+ def get(key, fallback = nil)
25
+ if self.key?(normalize_key(key))
26
+ self[normalize_key(key)]
27
+ else
28
+ fallback
29
+ end
30
+ end
31
+
24
32
  def method_missing(name, *args)
25
33
  super(normalize_key(name), *args.map { |arg| normalize_value(arg) })
26
34
  end