lookbook 1.0.0.beta.3 → 1.0.0.beta.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -254
- data/app/assets/lookbook/css/lookbook.css +1 -1
- data/app/assets/lookbook/css/themes/blue.css +57 -36
- data/app/assets/lookbook/css/themes/indigo.css +57 -36
- data/app/assets/lookbook/css/themes/zinc.css +57 -36
- data/app/assets/lookbook/css/tooltip.css +5 -5
- data/app/components/lookbook/base_component.rb +6 -4
- data/app/components/lookbook/button/component.html.erb +2 -2
- data/app/components/lookbook/copy_button/component.html.erb +3 -3
- data/app/components/lookbook/embed/component.html.erb +33 -33
- data/app/components/lookbook/embed/component.rb +8 -0
- data/app/components/lookbook/filter/component.html.erb +1 -1
- data/app/components/lookbook/header/component.html.erb +4 -4
- data/app/components/lookbook/header/component.rb +5 -0
- data/app/components/lookbook/inspector_panel/component.html.erb +4 -0
- data/app/components/lookbook/inspector_panel/component.rb +36 -0
- data/app/components/lookbook/nav/component.html.erb +4 -8
- data/app/components/lookbook/nav/component.rb +5 -7
- data/app/components/lookbook/nav/item/component.html.erb +12 -6
- data/app/components/lookbook/nav/item/component.rb +9 -2
- data/app/components/lookbook/page_tabs/component.html.erb +1 -1
- data/app/components/lookbook/prose/component.html.erb +1 -1
- data/app/components/lookbook/split_layout/component.html.erb +1 -1
- data/app/components/lookbook/tab_panels/panel/component.html.erb +2 -2
- data/app/components/lookbook/tabs/component.html.erb +2 -2
- data/app/components/lookbook/tabs/dropdown_tab/component.html.erb +2 -2
- data/app/components/lookbook/tabs/tab/component.html.erb +2 -2
- data/app/components/lookbook/tag_component.rb +5 -10
- data/app/components/lookbook/toolbar/component.html.erb +1 -1
- data/app/components/lookbook/viewport/component.css +1 -1
- data/app/components/lookbook/viewport/component.html.erb +1 -1
- data/app/controllers/lookbook/application_controller.rb +1 -1
- data/app/controllers/lookbook/pages_controller.rb +1 -0
- data/app/controllers/lookbook/previews_controller.rb +15 -3
- data/app/helpers/lookbook/application_helper.rb +0 -6
- data/app/helpers/lookbook/preview_helper.rb +2 -3
- data/app/views/layouts/lookbook/application.html.erb +76 -58
- data/app/views/layouts/lookbook/page.html.erb +36 -36
- data/app/views/layouts/lookbook/shell.html.erb +8 -48
- data/app/views/layouts/lookbook/skeleton.html.erb +0 -6
- data/app/views/lookbook/index.html.erb +24 -11
- data/app/views/lookbook/pages/show.html.erb +5 -5
- data/app/views/lookbook/previews/panels/_content.html.erb +1 -1
- data/app/views/lookbook/previews/panels/_notes.html.erb +2 -2
- data/app/views/lookbook/previews/panels/_params.html.erb +1 -1
- data/app/views/lookbook/previews/show.html.erb +12 -10
- data/lib/lookbook/config.rb +107 -74
- data/lib/lookbook/engine.rb +24 -6
- data/lib/lookbook/markdown.rb +3 -1
- data/lib/lookbook/page.rb +4 -0
- data/lib/lookbook/params.rb +12 -0
- data/lib/lookbook/preview.rb +35 -5
- data/lib/lookbook/store.rb +8 -0
- data/lib/lookbook/version.rb +1 -1
- data/lib/tasks/lookbook_tasks.rake +1 -1
- data/public/lookbook-assets/css/lookbook.css +330 -182
- data/public/lookbook-assets/css/lookbook.css.map +1 -1
- data/public/lookbook-assets/css/themes/blue.css +57 -36
- data/public/lookbook-assets/css/themes/blue.css.map +1 -1
- data/public/lookbook-assets/css/themes/indigo.css +57 -36
- data/public/lookbook-assets/css/themes/indigo.css.map +1 -1
- data/public/lookbook-assets/css/themes/zinc.css +57 -36
- data/public/lookbook-assets/css/themes/zinc.css.map +1 -1
- data/public/lookbook-assets/js/embed.js +193 -193
- data/public/lookbook-assets/js/lookbook.js +1179 -1336
- data/public/lookbook-assets/js/lookbook.js.map +1 -1
- metadata +6 -4
data/lib/lookbook/config.rb
CHANGED
@@ -6,7 +6,7 @@ module Lookbook
|
|
6
6
|
class Config
|
7
7
|
def initialize
|
8
8
|
@options = Store.new
|
9
|
-
|
9
|
+
|
10
10
|
@options.set({
|
11
11
|
project_name: "Lookbook",
|
12
12
|
log_level: 2,
|
@@ -22,12 +22,12 @@ module Lookbook
|
|
22
22
|
|
23
23
|
preview_paths: [],
|
24
24
|
preview_display_params: {},
|
25
|
-
|
26
|
-
|
27
|
-
sort_examples: true,
|
25
|
+
preview_srcdoc: nil,
|
26
|
+
sort_examples: false,
|
28
27
|
|
29
28
|
listen: Rails.env.development?,
|
30
29
|
listen_paths: [],
|
30
|
+
listen_extensions: ["rb", "html.*"],
|
31
31
|
listen_use_polling: false,
|
32
32
|
|
33
33
|
cable_mount_path: "/lookbook-cable",
|
@@ -45,6 +45,8 @@ module Lookbook
|
|
45
45
|
after_change: [],
|
46
46
|
},
|
47
47
|
|
48
|
+
debug_menu: Rails.env.development?,
|
49
|
+
|
48
50
|
experimental_features: false,
|
49
51
|
|
50
52
|
inspector_panels: {
|
@@ -54,7 +56,8 @@ module Lookbook
|
|
54
56
|
partial: "lookbook/previews/panels/preview",
|
55
57
|
hotkey: "v",
|
56
58
|
panel_classes: "overflow-hidden",
|
57
|
-
padded: false
|
59
|
+
padded: false,
|
60
|
+
system: true
|
58
61
|
},
|
59
62
|
output: {
|
60
63
|
pane: :main,
|
@@ -62,7 +65,8 @@ module Lookbook
|
|
62
65
|
partial: "lookbook/previews/panels/output",
|
63
66
|
label: "HTML",
|
64
67
|
hotkey: "h",
|
65
|
-
padded: false
|
68
|
+
padded: false,
|
69
|
+
system: true
|
66
70
|
},
|
67
71
|
source: {
|
68
72
|
pane: :drawer,
|
@@ -71,7 +75,8 @@ module Lookbook
|
|
71
75
|
label: "Source",
|
72
76
|
hotkey: "s",
|
73
77
|
copy: ->(data) { data.examples.map { |e| e.source }.join("\n") },
|
74
|
-
padded: false
|
78
|
+
padded: false,
|
79
|
+
system: true
|
75
80
|
},
|
76
81
|
notes: {
|
77
82
|
pane: :drawer,
|
@@ -80,7 +85,8 @@ module Lookbook
|
|
80
85
|
label: "Notes",
|
81
86
|
hotkey: "n",
|
82
87
|
disabled: ->(data) { data.examples.select { |e| e.notes.present? }.none? },
|
83
|
-
padded: false
|
88
|
+
padded: false,
|
89
|
+
system: true
|
84
90
|
},
|
85
91
|
params: {
|
86
92
|
pane: :drawer,
|
@@ -89,56 +95,80 @@ module Lookbook
|
|
89
95
|
label: "Params",
|
90
96
|
hotkey: "p",
|
91
97
|
disabled: ->(data) { data.preview.params.none? },
|
92
|
-
padded: false
|
98
|
+
padded: false,
|
99
|
+
system: true
|
93
100
|
}
|
94
101
|
},
|
95
|
-
|
96
|
-
inspector_panel_defaults: {
|
97
|
-
id: ->(data) { "inspector-panel-#{data.name}" },
|
98
|
-
partial: "lookbook/previews/panels/content",
|
99
|
-
content: nil,
|
100
|
-
label: ->(data) { data.name.titleize },
|
101
|
-
pane: :drawer,
|
102
|
-
position: ->(data) { data.index_position },
|
103
|
-
hotkey: nil,
|
104
|
-
disabled: false,
|
105
|
-
show: true,
|
106
|
-
copy: nil,
|
107
|
-
panel_classes: nil,
|
108
|
-
locals: {},
|
109
|
-
padded: true
|
110
|
-
},
|
111
102
|
})
|
112
103
|
end
|
113
104
|
|
105
|
+
def project_name
|
106
|
+
@options.project_name == false ? nil : @options.project_name
|
107
|
+
end
|
108
|
+
|
109
|
+
def components_path
|
110
|
+
absolute_path(@options.components_path)
|
111
|
+
end
|
112
|
+
|
113
|
+
def page_paths=(paths = [])
|
114
|
+
@options.page_paths += paths if paths.is_a? Array
|
115
|
+
end
|
116
|
+
|
117
|
+
def page_paths
|
118
|
+
normalize_paths(@options.page_paths)
|
119
|
+
end
|
120
|
+
|
121
|
+
def preview_paths=(paths = [])
|
122
|
+
@options.preview_paths += paths if paths.is_a? Array
|
123
|
+
end
|
124
|
+
|
125
|
+
def preview_paths
|
126
|
+
normalize_paths(@options.preview_paths)
|
127
|
+
end
|
128
|
+
|
129
|
+
def preview_srcdoc=(enable)
|
130
|
+
Lookbook.logger.warn "The `preview_srcdoc` config option is deprecated and will be removed in v2.0"
|
131
|
+
end
|
132
|
+
|
133
|
+
def listen_paths
|
134
|
+
normalize_paths(@options.listen_paths)
|
135
|
+
end
|
136
|
+
|
137
|
+
def listen_extensions=(extensions = [])
|
138
|
+
@options.listen_extensions += extensions if extensions.is_a? Array
|
139
|
+
@options.listen_extensions.uniq!
|
140
|
+
end
|
141
|
+
|
142
|
+
def parser_registry_path
|
143
|
+
absolute_path(@options.parser_registry_path)
|
144
|
+
end
|
145
|
+
|
114
146
|
def inspector_panels(&block)
|
147
|
+
panels = Store.new(@options.inspector_panels.select { |key, panel| panel != false })
|
115
148
|
if block_given?
|
116
|
-
yield
|
149
|
+
yield panels
|
117
150
|
else
|
118
|
-
|
151
|
+
panels
|
119
152
|
end
|
120
153
|
end
|
121
154
|
|
122
155
|
def define_inspector_panel(name, opts = {})
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
if panel.position >= opts[:position]
|
131
|
-
panel.position += 1
|
132
|
-
end
|
156
|
+
pane = opts[:pane].presence || :drawer
|
157
|
+
siblings = inspector_panels.select { |key, panel| panel.pane == pane }
|
158
|
+
opts[:position] ||= siblings.size + 1
|
159
|
+
@options.inspector_panels[name] = opts
|
160
|
+
siblings.each do |key, panel|
|
161
|
+
if panel.position >= opts[:position]
|
162
|
+
panel.position += 1
|
133
163
|
end
|
134
164
|
end
|
135
165
|
end
|
136
166
|
|
137
167
|
def amend_inspector_panel(name, opts = {})
|
138
168
|
if opts == false
|
139
|
-
inspector_panels[name] = false
|
169
|
+
@options.inspector_panels[name] = false
|
140
170
|
else
|
141
|
-
inspector_panels[name].merge!(opts)
|
171
|
+
@options.inspector_panels[name].merge!(opts)
|
142
172
|
end
|
143
173
|
end
|
144
174
|
|
@@ -146,10 +176,29 @@ module Lookbook
|
|
146
176
|
amend_inspector_panel(name, false)
|
147
177
|
end
|
148
178
|
|
179
|
+
def inspector_panel_defaults
|
180
|
+
{
|
181
|
+
id: ->(data) { "inspector-panel-#{data.name}" },
|
182
|
+
partial: "lookbook/previews/panels/content",
|
183
|
+
content: nil,
|
184
|
+
label: ->(data) { data.name.titleize },
|
185
|
+
pane: :drawer,
|
186
|
+
position: ->(data) { data.index_position },
|
187
|
+
hotkey: nil,
|
188
|
+
disabled: false,
|
189
|
+
show: true,
|
190
|
+
copy: nil,
|
191
|
+
panel_classes: nil,
|
192
|
+
locals: {},
|
193
|
+
padded: true,
|
194
|
+
system: false
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
149
198
|
def ui_theme=(name)
|
150
199
|
name = name.to_s
|
151
200
|
if Theme.valid_theme?(name)
|
152
|
-
@options
|
201
|
+
@options.ui_theme = name
|
153
202
|
else
|
154
203
|
Lookbook.logger.warn "'#{name}' is not a valid Lookbook theme. Theme setting not changed."
|
155
204
|
end
|
@@ -157,18 +206,27 @@ module Lookbook
|
|
157
206
|
|
158
207
|
def ui_theme_overrides(&block)
|
159
208
|
if block_given?
|
160
|
-
yield
|
209
|
+
yield @options.ui_theme_overrides
|
161
210
|
else
|
162
|
-
|
211
|
+
@options.ui_theme_overrides
|
163
212
|
end
|
164
213
|
end
|
165
214
|
|
166
215
|
def [](key)
|
167
|
-
|
216
|
+
if respond_to? key.to_sym
|
217
|
+
public_send(key.to_sym)
|
218
|
+
else
|
219
|
+
@options[key.to_sym]
|
220
|
+
end
|
168
221
|
end
|
169
222
|
|
170
223
|
def []=(key, value)
|
171
|
-
|
224
|
+
setter_key = "#{key}=".to_sym
|
225
|
+
if respond_to? setter_key
|
226
|
+
public_send(setter_key, value)
|
227
|
+
else
|
228
|
+
@options[key.to_sym] = value
|
229
|
+
end
|
172
230
|
end
|
173
231
|
|
174
232
|
def to_h
|
@@ -181,22 +239,11 @@ module Lookbook
|
|
181
239
|
|
182
240
|
protected
|
183
241
|
|
184
|
-
def get_inspector_panels(panels)
|
185
|
-
panels.select! { |key, panel| panel }
|
186
|
-
panels
|
187
|
-
end
|
188
|
-
|
189
|
-
def get_project_name(name)
|
190
|
-
name == false ? nil : name
|
191
|
-
end
|
192
|
-
|
193
|
-
def get_components_path(path)
|
194
|
-
absolute_path(path)
|
195
|
-
end
|
196
|
-
|
197
242
|
def normalize_paths(paths)
|
198
|
-
paths.map!
|
199
|
-
|
243
|
+
paths.map! do |path|
|
244
|
+
full_path = absolute_path(path)
|
245
|
+
full_path if Dir.exist?(full_path)
|
246
|
+
end.compact!
|
200
247
|
paths
|
201
248
|
end
|
202
249
|
|
@@ -204,22 +251,8 @@ module Lookbook
|
|
204
251
|
File.absolute_path(path.to_s, Rails.root)
|
205
252
|
end
|
206
253
|
|
207
|
-
alias_method :get_page_paths, :normalize_paths
|
208
|
-
alias_method :get_preview_paths, :normalize_paths
|
209
|
-
alias_method :get_listen_paths, :normalize_paths
|
210
|
-
alias_method :get_parser_registry_path, :absolute_path
|
211
|
-
|
212
|
-
def get(name)
|
213
|
-
getter_name = "get_#{name}".to_sym
|
214
|
-
respond_to?(getter_name, true) ? send(getter_name, @options[name]) : @options[name]
|
215
|
-
end
|
216
|
-
|
217
|
-
def set(name, *args)
|
218
|
-
@options.send(name, *args)
|
219
|
-
end
|
220
|
-
|
221
254
|
def method_missing(name, *args)
|
222
|
-
|
255
|
+
@options.send(name, *args)
|
223
256
|
end
|
224
257
|
end
|
225
258
|
end
|
data/lib/lookbook/engine.rb
CHANGED
@@ -36,10 +36,26 @@ module Lookbook
|
|
36
36
|
Preview.all
|
37
37
|
end
|
38
38
|
|
39
|
+
def previews?
|
40
|
+
Preview.any?
|
41
|
+
end
|
42
|
+
|
39
43
|
def pages
|
40
44
|
Page.all
|
41
45
|
end
|
42
46
|
|
47
|
+
def pages?
|
48
|
+
Page.any?
|
49
|
+
end
|
50
|
+
|
51
|
+
def data
|
52
|
+
@data ||= Store.new
|
53
|
+
end
|
54
|
+
|
55
|
+
def data=(props)
|
56
|
+
@data = Store.new(props)
|
57
|
+
end
|
58
|
+
|
43
59
|
def after_initialize(&block)
|
44
60
|
add_hook(:after_initialize, block)
|
45
61
|
end
|
@@ -112,10 +128,11 @@ module Lookbook
|
|
112
128
|
|
113
129
|
if config.lookbook.listen
|
114
130
|
Listen.logger = Lookbook.logger
|
131
|
+
|
115
132
|
preview_listener = Listen.to(
|
116
133
|
*config.lookbook.listen_paths,
|
117
|
-
only: /\.(
|
118
|
-
force_polling:
|
134
|
+
only: /\.(#{config.lookbook.listen_extensions.join("|")})$/,
|
135
|
+
force_polling: config.lookbook.listen_use_polling
|
119
136
|
) do |modified, added, removed|
|
120
137
|
changes = { modified: modified, added: added, removed: removed }
|
121
138
|
begin
|
@@ -131,7 +148,7 @@ module Lookbook
|
|
131
148
|
page_listener = Listen.to(
|
132
149
|
*config.lookbook.page_paths,
|
133
150
|
only: /\.(html.*|md.*)$/,
|
134
|
-
force_polling:
|
151
|
+
force_polling: config.lookbook.listen_use_polling
|
135
152
|
) do |modified, added, removed|
|
136
153
|
changes = { modified: modified, added: added, removed: removed }
|
137
154
|
Lookbook::Engine.reload_ui(changes)
|
@@ -166,6 +183,7 @@ module Lookbook
|
|
166
183
|
class << self
|
167
184
|
|
168
185
|
def websocket
|
186
|
+
return @websocket unless @websocket.nil?
|
169
187
|
if config.lookbook.auto_refresh
|
170
188
|
cable = ActionCable::Server::Configuration.new
|
171
189
|
cable.cable = {adapter: "async"}.with_indifferent_access
|
@@ -176,9 +194,9 @@ module Lookbook
|
|
176
194
|
@websocket ||= if Rails.version.to_f >= 6.0
|
177
195
|
ActionCable::Server::Base.new(config: cable)
|
178
196
|
else
|
179
|
-
websocket ||= ActionCable::Server::Base.new
|
180
|
-
websocket.config = cable
|
181
|
-
websocket
|
197
|
+
@websocket ||= ActionCable::Server::Base.new
|
198
|
+
@websocket.config = cable
|
199
|
+
@websocket
|
182
200
|
end
|
183
201
|
end
|
184
202
|
end
|
data/lib/lookbook/markdown.rb
CHANGED
@@ -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
data/lib/lookbook/params.rb
CHANGED
@@ -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",
|
data/lib/lookbook/preview.rb
CHANGED
@@ -122,14 +122,15 @@ module Lookbook
|
|
122
122
|
!!find(path)
|
123
123
|
end
|
124
124
|
|
125
|
-
def
|
126
|
-
|
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
|
-
|
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
|
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|
|
data/lib/lookbook/store.rb
CHANGED
@@ -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
|
data/lib/lookbook/version.rb
CHANGED