outro_rails 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +117 -11
- data/app/assets/stylesheets/outro_rails/outro_rails.css +17 -43
- data/app/helpers/outro_rails/chord_charts_helper.rb +10 -3
- data/app/helpers/outro_rails/class_names_helper.rb +34 -0
- data/app/models/outro_rails/chord_chart.rb +11 -1
- data/app/views/outro_rails/caged/show.html.erb +5 -5
- data/app/views/outro_rails/capo/show.html.erb +2 -1
- data/app/views/outro_rails/chord_charts/_chart.html.erb +12 -9
- data/app/views/outro_rails/chord_charts/_fields.html.erb +46 -12
- data/app/views/outro_rails/chords/_filter.html.erb +14 -14
- data/app/views/outro_rails/chords/index.html.erb +1 -1
- data/app/views/outro_rails/circle_of_fifths/show.html.erb +2 -3
- data/app/views/outro_rails/instruments/_guitar.html.erb +3 -3
- data/app/views/outro_rails/scales/_picker.html.erb +7 -7
- data/app/views/outro_rails/scales/show.html.erb +2 -3
- data/app/views/outro_rails/transposer/show.html.erb +9 -9
- data/lib/outro_rails/chord_chart_attribute.rb +57 -5
- data/lib/outro_rails/version.rb +1 -1
- data/lib/outro_rails.rb +31 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9a61b44de4d831dd78c2ef49903677777792cfe812a6f77211dbf4e846ffb35
|
|
4
|
+
data.tar.gz: 60ee9af6996b28da00252dc296e40d5869c0f877ed8335e126d8650ae6b8d462
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 153b0d96d98f43b44c323a06fa7b3106b2ea06d0e92ca2c4a9e33f74bb3a6555b109bffbb880c3969d5147ca0aa3e43ba573060111b073c36e80f7958911c4ac
|
|
7
|
+
data.tar.gz: 91a29abfb552a60757fcdc0ded4883d921000fa9fb2c2a31e4a855e5cca628689b9def02844da3f88f736fba6c44ac4b9ff4e5280d50bb736b532bd6b5d67096
|
data/README.md
CHANGED
|
@@ -50,7 +50,8 @@ quickest version is to add two tags to your layout:
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
If you'd rather fold the engine's assets into your existing bundles, see
|
|
53
|
-
[Assets](#assets) below.
|
|
53
|
+
[Assets](#assets) below. If you'd rather style the form controls with your
|
|
54
|
+
own CSS framework, see [Control classes](#control-classes).
|
|
54
55
|
|
|
55
56
|
## Assets
|
|
56
57
|
|
|
@@ -161,6 +162,61 @@ yarn build
|
|
|
161
162
|
yarn build:css
|
|
162
163
|
```
|
|
163
164
|
|
|
165
|
+
### Form Control Classes
|
|
166
|
+
|
|
167
|
+
Out of the box the engine's text fields, textareas, selects, and labels
|
|
168
|
+
carry its own classes, styled by the stylesheet above:
|
|
169
|
+
|
|
170
|
+
| Element | Setting | Default |
|
|
171
|
+
| ---------- | -------------------------- | ---------------- |
|
|
172
|
+
| `button` | `OutroRails.button_class` | `outro-button` |
|
|
173
|
+
| `input` | `OutroRails.input_class` | `outro-input` |
|
|
174
|
+
| `label` | `OutroRails.label_class` | `outro-label` |
|
|
175
|
+
| `select` | `OutroRails.select_class` | `outro-select` |
|
|
176
|
+
| `textarea` | `OutroRails.textarea_class` | `outro-textarea` |
|
|
177
|
+
|
|
178
|
+
If your app already has a form style (Bootstrap, Tailwind, your own classes)
|
|
179
|
+
point these at it in an initializer and the engine will render your classes
|
|
180
|
+
instead:
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
# config/initializers/outro_rails.rb
|
|
184
|
+
OutroRails.configure do |config|
|
|
185
|
+
config.button_class = "btn btn-sm"
|
|
186
|
+
config.input_class = "form-control"
|
|
187
|
+
config.label_class = "form-label"
|
|
188
|
+
config.select_class = "form-select"
|
|
189
|
+
config.textarea_class = "form-control"
|
|
190
|
+
end
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Each setting is a plain string and can also be assigned on its own
|
|
194
|
+
(`OutroRails.select_class = "form-select"`).
|
|
195
|
+
|
|
196
|
+
These apply everywhere the engine renders a form control.
|
|
197
|
+
|
|
198
|
+
#### Per render
|
|
199
|
+
|
|
200
|
+
The same four names are accepted as locals on the chart partial and as
|
|
201
|
+
options on `chord_chart_area` for a page that needs something other than
|
|
202
|
+
the application-wide setting:
|
|
203
|
+
|
|
204
|
+
```erb
|
|
205
|
+
<%= form.chord_chart_area :chord_sheet,
|
|
206
|
+
button_class: "btn-primary",
|
|
207
|
+
input_class: "form-control",
|
|
208
|
+
label_class: "form-label",
|
|
209
|
+
select_class: "form-select",
|
|
210
|
+
textarea_class: "form-control font-monospace" %>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```erb
|
|
214
|
+
<%= render "outro_rails/chord_charts/chart",
|
|
215
|
+
chord_chart: @song.chord_sheet,
|
|
216
|
+
select_class: "form-select",
|
|
217
|
+
label_class: "form-label" %>
|
|
218
|
+
```
|
|
219
|
+
|
|
164
220
|
## Usage
|
|
165
221
|
To view the chord library and music theory tools, mount the engine in your
|
|
166
222
|
routes.rb:
|
|
@@ -200,9 +256,38 @@ This gives the model `chord_sheet`/`chord_sheet=`/`chord_sheet?`, backed by a
|
|
|
200
256
|
(title), `tuning`, `capo`, `key`, and `body`). A model can attach more
|
|
201
257
|
than one chart under different names.
|
|
202
258
|
|
|
259
|
+
#### Required or Optional
|
|
260
|
+
|
|
261
|
+
By default the chart is required: `song.chord_sheet` always returns a chart
|
|
262
|
+
(building an unsaved one on first access), and saving the record with the
|
|
263
|
+
artist or song blank fails validation with errors.
|
|
264
|
+
|
|
265
|
+
Pass `required: false` to let the chart be left out entirely:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
class Song < ApplicationRecord
|
|
269
|
+
has_chord_chart :chord_sheet, required: false
|
|
270
|
+
end
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
With an optional chart:
|
|
274
|
+
|
|
275
|
+
* `song.chord_sheet` returns `nil` until someone fills it in, and
|
|
276
|
+
`song.chord_sheet?` reports whether one exists.
|
|
277
|
+
* A form submitted with the artist, song, and body all blank saves the
|
|
278
|
+
record with no chart. Filling in *some* of those fields still
|
|
279
|
+
validates as usual, so the user sees what's missing.
|
|
280
|
+
* Clearing every field on a record that already had a chart removes that
|
|
281
|
+
chart when the record saves. If the record itself fails validation,
|
|
282
|
+
nothing is deleted.
|
|
283
|
+
* `song.chord_sheet = nil` removes a chart; `song.chord_sheet = { ... }`
|
|
284
|
+
builds one if needed and assigns the given attributes.
|
|
285
|
+
|
|
286
|
+
`song.chord_sheet_required?` reports which way the model was declared.
|
|
287
|
+
|
|
203
288
|
### Forms: chord_chart_area
|
|
204
289
|
|
|
205
|
-
In a form,
|
|
290
|
+
In a form, use the `chord_chart_area` form builder method:
|
|
206
291
|
|
|
207
292
|
```erb
|
|
208
293
|
<%= form_with model: @song do |form| %>
|
|
@@ -211,10 +296,29 @@ In a form, pair it with the `chord_chart_area` form builder method:
|
|
|
211
296
|
<% end %>
|
|
212
297
|
```
|
|
213
298
|
|
|
214
|
-
`chord_chart_area` renders
|
|
215
|
-
options are pulled from
|
|
216
|
-
|
|
217
|
-
|
|
299
|
+
`chord_chart_area` renders a fieldset with a heading and
|
|
300
|
+
artist/song/tuning/capo/key/body fields. Tuning options are pulled from
|
|
301
|
+
`Instruments::Guitar::Tunings`, capo options are capped at fret 7
|
|
302
|
+
(`Instruments::Guitar::Capo::FRETS`). Key options are set from
|
|
303
|
+
`Theory::Key`.
|
|
304
|
+
|
|
305
|
+
The heading defaults to "Chord Chart". Set `legend:` to change it, or pass
|
|
306
|
+
`false` when the surrounding page already has a heading for this section:
|
|
307
|
+
|
|
308
|
+
```erb
|
|
309
|
+
<%= form.chord_chart_area :chord_sheet, legend: "Lead sheet" %>
|
|
310
|
+
<%= form.chord_chart_area :chord_sheet, legend: false %>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
When the model declares the chart with `required: false`, the fieldset is
|
|
314
|
+
labeled "Optional" and tells the user to leave the fields blank if there's
|
|
315
|
+
no chord chart.
|
|
316
|
+
|
|
317
|
+
The fields inside the fieldset use the engine's own control classes. Pass
|
|
318
|
+
`input_class:`, `textarea_class:`, `select_class:`, or `label_class:` to
|
|
319
|
+
render them with your app's classes for this form, or set them once
|
|
320
|
+
application-wide — see [Control classes](#control-classes). Any other
|
|
321
|
+
option is passed through to `fields_for`, as before.
|
|
218
322
|
|
|
219
323
|
### Controller: Permitting the Parameters
|
|
220
324
|
|
|
@@ -233,15 +337,13 @@ class SongsController < ApplicationController
|
|
|
233
337
|
def song_params
|
|
234
338
|
params.require(:song).permit(
|
|
235
339
|
:description,
|
|
236
|
-
chord_chart_chord_sheet_attributes: %i[artist body capo key song tuning]
|
|
340
|
+
chord_chart_chord_sheet_attributes: %i[id artist body capo key song tuning]
|
|
237
341
|
)
|
|
238
342
|
end
|
|
239
343
|
end
|
|
240
344
|
```
|
|
241
345
|
|
|
242
|
-
|
|
243
|
-
renders. `record` and `name` are managed by the engine and should not be
|
|
244
|
-
permitted. If a model attaches more than one chart, each one needs its own
|
|
346
|
+
If a model attaches more than one chart, each one needs its own
|
|
245
347
|
key: `has_chord_chart :lead_sheet` is permitted as
|
|
246
348
|
`chord_chart_lead_sheet_attributes`.
|
|
247
349
|
|
|
@@ -264,6 +366,11 @@ This renders the chart's metadata, an instrument toggle, a transpose-to-key
|
|
|
264
366
|
picker, the chart body with recognized chords highlighted, and a
|
|
265
367
|
diagram for every chord the body references.
|
|
266
368
|
|
|
369
|
+
The partial accepts `chord_chart: nil`, so it can be handed an optional
|
|
370
|
+
chart that hasn't been filled in yet; it renders a short "No chord chart."
|
|
371
|
+
placeholder in that case. Check `@song.chord_sheet?` instead if you'd
|
|
372
|
+
rather show nothing at all.
|
|
373
|
+
|
|
267
374
|
### Using Your Own Layout
|
|
268
375
|
|
|
269
376
|
By default the engine renders its pages in its own layout,
|
|
@@ -426,7 +533,6 @@ It seeds the chord library and four example songs (`spec/dummy/db/seeds.rb`).
|
|
|
426
533
|
The example songs are listed on the home page. To illustrate permissions,
|
|
427
534
|
the dummy app sets even record ids as editable and odd ones as view-only.
|
|
428
535
|
|
|
429
|
-
|
|
430
536
|
## Publishing a New Version
|
|
431
537
|
|
|
432
538
|
**1. Bump the version.** Edit `lib/outro_rails/version.rb`
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
from `currentColor`, so they adapt to light and dark host themes.
|
|
12
12
|
========================================================================== */
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
.outro-page {
|
|
14
|
+
:root {
|
|
16
15
|
--outro-container-width: 66rem;
|
|
17
16
|
--outro-radius: 14px;
|
|
18
17
|
--outro-radius-sm: 6px;
|
|
@@ -54,8 +53,7 @@
|
|
|
54
53
|
|
|
55
54
|
@supports not (color: color-mix(in srgb, currentColor 50%, transparent)) {
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
.outro-page {
|
|
56
|
+
:root {
|
|
59
57
|
--outro-muted: rgba(128, 128, 128, 0.9);
|
|
60
58
|
--outro-border: rgba(128, 128, 128, 0.3);
|
|
61
59
|
--outro-border-strong: rgba(128, 128, 128, 0.5);
|
|
@@ -129,8 +127,6 @@
|
|
|
129
127
|
align-items: center;
|
|
130
128
|
gap: 0.4rem;
|
|
131
129
|
min-width: 0;
|
|
132
|
-
align-items: center;
|
|
133
|
-
min-width: 0;
|
|
134
130
|
margin: 0;
|
|
135
131
|
padding: 0.3rem clamp(0.6rem, 2vw, 0.95rem);
|
|
136
132
|
border: 1px solid var(--outro-border);
|
|
@@ -307,21 +303,6 @@
|
|
|
307
303
|
font: inherit;
|
|
308
304
|
}
|
|
309
305
|
|
|
310
|
-
.outro-button {
|
|
311
|
-
display: inline-flex;
|
|
312
|
-
align-items: center;
|
|
313
|
-
gap: 0.45rem;
|
|
314
|
-
padding: 0.4rem 0.9rem;
|
|
315
|
-
border: 1px solid var(--outro-border-strong);
|
|
316
|
-
border-radius: var(--outro-radius-sm);
|
|
317
|
-
background-color: var(--outro-surface, transparent);
|
|
318
|
-
color: inherit;
|
|
319
|
-
font: inherit;
|
|
320
|
-
line-height: 1.2;
|
|
321
|
-
text-decoration: none;
|
|
322
|
-
cursor: pointer;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
306
|
.outro-card--link {
|
|
326
307
|
transition: transform 0.16s ease, box-shadow 0.16s ease,
|
|
327
308
|
border-color 0.16s ease;
|
|
@@ -415,13 +396,6 @@
|
|
|
415
396
|
}
|
|
416
397
|
}
|
|
417
398
|
|
|
418
|
-
.outro-panel {
|
|
419
|
-
padding: var(--outro-card-padding);
|
|
420
|
-
border: 1px solid var(--outro-border);
|
|
421
|
-
border-radius: var(--outro-radius);
|
|
422
|
-
background-color: var(--outro-surface, transparent);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
399
|
.outro-empty {
|
|
426
400
|
padding: clamp(1.25rem, 4vw, 2rem);
|
|
427
401
|
border: 1px dashed var(--outro-border-strong);
|
|
@@ -438,17 +412,6 @@
|
|
|
438
412
|
margin-bottom: 0;
|
|
439
413
|
}
|
|
440
414
|
|
|
441
|
-
.outro-select,
|
|
442
|
-
.outro-input,
|
|
443
|
-
.outro-textarea {
|
|
444
|
-
padding: var(--outro-control-padding);
|
|
445
|
-
border: 1px solid var(--outro-border-strong);
|
|
446
|
-
border-radius: var(--outro-radius-sm);
|
|
447
|
-
background-color: var(--outro-surface, transparent);
|
|
448
|
-
color: inherit;
|
|
449
|
-
font: inherit;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
415
|
.outro-button {
|
|
453
416
|
display: inline-flex;
|
|
454
417
|
align-items: center;
|
|
@@ -727,7 +690,6 @@
|
|
|
727
690
|
.outro-guitar {
|
|
728
691
|
display: flex;
|
|
729
692
|
flex-direction: column;
|
|
730
|
-
align-items: center;
|
|
731
693
|
gap: 14px;
|
|
732
694
|
width: 100%;
|
|
733
695
|
max-width: 100%;
|
|
@@ -735,7 +697,6 @@
|
|
|
735
697
|
border-radius: var(--outro-instrument-radius);
|
|
736
698
|
background-color: var(--outro-instrument-bg);
|
|
737
699
|
overflow-x: auto;
|
|
738
|
-
align-items: center;
|
|
739
700
|
align-items: safe center;
|
|
740
701
|
}
|
|
741
702
|
|
|
@@ -910,7 +871,6 @@
|
|
|
910
871
|
.outro-piano {
|
|
911
872
|
display: flex;
|
|
912
873
|
flex-direction: column;
|
|
913
|
-
align-items: center;
|
|
914
874
|
gap: 14px;
|
|
915
875
|
width: 100%;
|
|
916
876
|
max-width: 100%;
|
|
@@ -918,7 +878,6 @@
|
|
|
918
878
|
border-radius: var(--outro-instrument-radius);
|
|
919
879
|
background-color: var(--outro-instrument-bg);
|
|
920
880
|
overflow-x: auto;
|
|
921
|
-
align-items: center;
|
|
922
881
|
align-items: safe center;
|
|
923
882
|
}
|
|
924
883
|
|
|
@@ -1207,6 +1166,15 @@
|
|
|
1207
1166
|
position: relative;
|
|
1208
1167
|
}
|
|
1209
1168
|
|
|
1169
|
+
.outro-chord-chart-fields__legend {
|
|
1170
|
+
float: none;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
legend.outro-chord-chart-fields__legend {
|
|
1174
|
+
float: none;
|
|
1175
|
+
margin-bottom: 0;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1210
1178
|
.outro-chord-chart__body {
|
|
1211
1179
|
margin: 0;
|
|
1212
1180
|
padding: 0.85rem;
|
|
@@ -1259,6 +1227,12 @@
|
|
|
1259
1227
|
border: 0;
|
|
1260
1228
|
}
|
|
1261
1229
|
|
|
1230
|
+
.outro-chord-chart-fields__hint {
|
|
1231
|
+
margin: 0;
|
|
1232
|
+
font-size: 0.9rem;
|
|
1233
|
+
color: var(--outro-muted);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1262
1236
|
.outro-inline-form {
|
|
1263
1237
|
display: inline;
|
|
1264
1238
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module OutroRails
|
|
4
4
|
module ChordChartsHelper
|
|
5
5
|
include CapoHelper
|
|
6
|
+
include ClassNamesHelper
|
|
6
7
|
include InstrumentsHelper
|
|
7
8
|
include TransposerHelper
|
|
8
9
|
|
|
@@ -24,13 +25,19 @@ module OutroRails
|
|
|
24
25
|
Instruments::ALL.map { |instrument| [ instrument.titleize, instrument ] }
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def chord_chart_area(form, method, **options)
|
|
28
|
-
|
|
28
|
+
def chord_chart_area(form, method, legend: "Chord chart", **options)
|
|
29
|
+
record = form.object
|
|
30
|
+
required = record.public_send(:"#{method}_required?")
|
|
31
|
+
|
|
32
|
+
chord_chart = record.public_send(method) ||
|
|
33
|
+
OutroRails::ChordChart.new(name: method.to_s)
|
|
29
34
|
|
|
30
35
|
form.fields_for(:"chord_chart_#{method}",
|
|
31
36
|
chord_chart, **options) do |chord_chart_form|
|
|
32
37
|
render partial: "outro_rails/chord_charts/fields",
|
|
33
|
-
locals: { f: chord_chart_form
|
|
38
|
+
locals: { f: chord_chart_form,
|
|
39
|
+
required: required,
|
|
40
|
+
legend: legend }
|
|
34
41
|
end
|
|
35
42
|
end
|
|
36
43
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OutroRails
|
|
4
|
+
module ClassNamesHelper
|
|
5
|
+
def outro_button_class(*modifiers)
|
|
6
|
+
outro_element_class(:button, *modifiers)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def outro_input_class(*modifiers)
|
|
10
|
+
outro_element_class(:input, *modifiers)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def outro_label_class(*modifiers)
|
|
14
|
+
outro_element_class(:label, *modifiers)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def outro_select_class(*modifiers)
|
|
18
|
+
outro_element_class(:select, *modifiers)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def outro_textarea_class(*modifiers)
|
|
22
|
+
outro_element_class(:textarea, *modifiers)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def outro_element_class(element, *modifiers)
|
|
26
|
+
tokens = [ OutroRails.class_for(element), *modifiers ]
|
|
27
|
+
.flat_map { |token| token.to_s.split(/\s+/) }
|
|
28
|
+
.reject(&:empty?)
|
|
29
|
+
.uniq
|
|
30
|
+
|
|
31
|
+
tokens.join(" ").presence
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -9,6 +9,8 @@ module OutroRails
|
|
|
9
9
|
Theory::Key.canonical(:minor).map(&:to_s)
|
|
10
10
|
).freeze
|
|
11
11
|
|
|
12
|
+
CONTENT_ATTRIBUTES = %w[artist song body].freeze
|
|
13
|
+
|
|
12
14
|
belongs_to :record, polymorphic: true, touch: true
|
|
13
15
|
|
|
14
16
|
has_many :voicings,
|
|
@@ -22,7 +24,6 @@ module OutroRails
|
|
|
22
24
|
validates :name, presence: true
|
|
23
25
|
validates :artist, presence: true
|
|
24
26
|
validates :song, presence: true
|
|
25
|
-
validates :body, presence: true
|
|
26
27
|
validates :tuning,
|
|
27
28
|
inclusion: { in: Instruments::Guitar::Tunings.slugs },
|
|
28
29
|
allow_blank: true
|
|
@@ -31,6 +32,15 @@ module OutroRails
|
|
|
31
32
|
allow_nil: true
|
|
32
33
|
validates :key, inclusion: { in: KEY_OPTIONS }, allow_blank: true
|
|
33
34
|
|
|
35
|
+
def self.blank_attributes?(attributes)
|
|
36
|
+
return true if attributes.nil?
|
|
37
|
+
|
|
38
|
+
attributes = attributes.to_h if attributes.respond_to?(:permitted?)
|
|
39
|
+
attributes = attributes.to_h.with_indifferent_access
|
|
40
|
+
|
|
41
|
+
CONTENT_ATTRIBUTES.all? { |field| attributes[field].blank? }
|
|
42
|
+
end
|
|
43
|
+
|
|
34
44
|
def to_key
|
|
35
45
|
Theory::Key.parse(key.presence || "C")
|
|
36
46
|
end
|
|
@@ -16,27 +16,27 @@
|
|
|
16
16
|
<%= form_with url: caged_picker_path, method: :get,
|
|
17
17
|
class: "outro-picker" do %>
|
|
18
18
|
<label class="outro-picker__field">
|
|
19
|
-
|
|
19
|
+
<%= tag.span "Root", class: outro_label_class %>
|
|
20
20
|
<%= select_tag :root,
|
|
21
21
|
options_for_select(scale_root_options, @root.path_slug),
|
|
22
|
-
class:
|
|
22
|
+
class: outro_select_class,
|
|
23
23
|
onchange: "this.form.requestSubmit()" %>
|
|
24
24
|
</label>
|
|
25
25
|
|
|
26
26
|
<label class="outro-picker__field">
|
|
27
|
-
|
|
27
|
+
<%= tag.span "Shape", class: outro_label_class %>
|
|
28
28
|
<%= select_tag :shape,
|
|
29
29
|
options_for_select(
|
|
30
30
|
OutroRails::Instruments::Guitar::ChordShapes::CAGED_LETTERS
|
|
31
31
|
.map { |l| [ "#{l} shape", l.downcase ] },
|
|
32
32
|
@letter.downcase
|
|
33
33
|
),
|
|
34
|
-
class:
|
|
34
|
+
class: outro_select_class,
|
|
35
35
|
onchange: "this.form.requestSubmit()" %>
|
|
36
36
|
</label>
|
|
37
37
|
|
|
38
38
|
<%= button_tag type: :submit, name: nil,
|
|
39
|
-
class:
|
|
39
|
+
class: outro_button_class,
|
|
40
40
|
"aria-label": "Show Shapes" do %>
|
|
41
41
|
<%= image_tag "outro_rails/shapes.svg", class: 'outro-button__icon' %>
|
|
42
42
|
Show Shapes
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
] %>
|
|
15
15
|
|
|
16
16
|
<div class="outro-panel outro-filters">
|
|
17
|
-
|
|
17
|
+
<%= tag.span "Capo position", id: "capo-position-label",
|
|
18
|
+
class: outro_label_class %>
|
|
18
19
|
<nav class="outro-chip-row outro-capo-frets"
|
|
19
20
|
aria-labelledby="capo-position-label">
|
|
20
21
|
<% OutroRails::Instruments::Guitar::Capo::FRETS.each do |fret| %>
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
<span class="outro-label">Instrument</span>
|
|
89
89
|
<%= select_tag :instrument,
|
|
90
90
|
options_for_select(chord_chart_instrument_options, instrument),
|
|
91
|
-
class:
|
|
91
|
+
class: outro_select_class,
|
|
92
92
|
onchange: "this.form.requestSubmit()" %>
|
|
93
93
|
<%= hidden_field_tag :to_key, to_key if to_key %>
|
|
94
94
|
<% end %>
|
|
@@ -99,15 +99,18 @@
|
|
|
99
99
|
<%= select_tag :to_key,
|
|
100
100
|
grouped_options_for_select(chord_chart_key_options,
|
|
101
101
|
to_key || chord_chart.key),
|
|
102
|
-
class:
|
|
102
|
+
class: outro_select_class,
|
|
103
103
|
onchange: "this.form.requestSubmit()" %>
|
|
104
104
|
<%= hidden_field_tag :instrument, instrument %>
|
|
105
105
|
<% end %>
|
|
106
106
|
|
|
107
107
|
<% if transposed %>
|
|
108
|
-
<%= link_to
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
<%= link_to url_for(request.query_parameters.except("to_key")),
|
|
109
|
+
class: outro_button_class do %>
|
|
110
|
+
<%= image_tag "outro_rails/undo.svg",
|
|
111
|
+
class: "outro-button__icon" %>
|
|
112
|
+
<%= "Reset to #{chord_chart.key}" %>
|
|
113
|
+
<% end %>
|
|
111
114
|
<% end %>
|
|
112
115
|
</div>
|
|
113
116
|
</section>
|
|
@@ -186,7 +189,7 @@
|
|
|
186
189
|
<% if all_voicings.many? %>
|
|
187
190
|
<div class="outro-voicing-cycler__nav">
|
|
188
191
|
<button type="button" data-voicing-prev
|
|
189
|
-
class="
|
|
192
|
+
class="<%= outro_button_class %>
|
|
190
193
|
outro-voicing-cycler__arrow"
|
|
191
194
|
aria-label=
|
|
192
195
|
"Previous voicing for <%= symbol %>">
|
|
@@ -195,7 +198,7 @@
|
|
|
195
198
|
<span class="outro-voicing-cycler__counter"
|
|
196
199
|
data-voicing-counter aria-live="polite"></span>
|
|
197
200
|
<button type="button" data-voicing-next
|
|
198
|
-
class="
|
|
201
|
+
class="<%= outro_button_class %>
|
|
199
202
|
outro-voicing-cycler__arrow"
|
|
200
203
|
aria-label=
|
|
201
204
|
"Next voicing for <%= symbol %>">
|
|
@@ -231,7 +234,7 @@
|
|
|
231
234
|
)
|
|
232
235
|
%>
|
|
233
236
|
<%= button_to revert_path,
|
|
234
|
-
method: :delete, class:
|
|
237
|
+
method: :delete, class: outro_button_class,
|
|
235
238
|
form_class: "outro-inline-form" do %>
|
|
236
239
|
<%= image_tag "outro_rails/undo.svg",
|
|
237
240
|
class: "outro-button__icon" %>
|
|
@@ -259,7 +262,7 @@
|
|
|
259
262
|
)
|
|
260
263
|
%>
|
|
261
264
|
<%= button_to revert_path,
|
|
262
|
-
method: :delete, class:
|
|
265
|
+
method: :delete, class: outro_button_class,
|
|
263
266
|
form_class: "outro-inline-form" do %>
|
|
264
267
|
<%= image_tag "outro_rails/undo.svg",
|
|
265
268
|
class: "outro-button__icon" %>
|
|
@@ -5,46 +5,80 @@
|
|
|
5
5
|
|
|
6
6
|
form_with model: @song do |form|
|
|
7
7
|
form.chord_chart_area :chord_sheet
|
|
8
|
+
form.chord_chart_area :chord_sheet, legend: "Lead sheet"
|
|
9
|
+
form.chord_chart_area :chord_sheet, legend: false
|
|
8
10
|
end
|
|
11
|
+
|
|
12
|
+
Locals:
|
|
13
|
+
f - the nested form builder for the chord chart
|
|
14
|
+
required - whether the owner declared the chart with `required: true`
|
|
15
|
+
(the default). When false, leaving every field blank
|
|
16
|
+
saves the record without a chart.
|
|
17
|
+
legend - heading shown above the fields, default "Chord chart".
|
|
18
|
+
Pass false to leave it out (when the host page already has
|
|
19
|
+
its own heading for this section).
|
|
20
|
+
%>
|
|
21
|
+
|
|
22
|
+
<%
|
|
23
|
+
required = local_assigns.fetch(:required, true)
|
|
24
|
+
legend = local_assigns.fetch(:legend, "Chord Chart")
|
|
9
25
|
%>
|
|
10
26
|
|
|
11
27
|
<fieldset class="outro-chord-chart-fields">
|
|
28
|
+
<% if legend %>
|
|
29
|
+
<legend class="outro-chord-chart-fields__legend">
|
|
30
|
+
<span class="outro-section-heading">
|
|
31
|
+
<%= legend %>
|
|
32
|
+
<% unless required %>
|
|
33
|
+
<span class="outro-symbol-list__detail">Optional</span>
|
|
34
|
+
<% end %>
|
|
35
|
+
</span>
|
|
36
|
+
</legend>
|
|
37
|
+
<% end %>
|
|
38
|
+
|
|
39
|
+
<% unless required %>
|
|
40
|
+
<p class="outro-chord-chart-fields__hint">
|
|
41
|
+
Leave artist, song, and chords blank if there is no chord chart.
|
|
42
|
+
</p>
|
|
43
|
+
<% end %>
|
|
44
|
+
|
|
12
45
|
<div class="outro-picker outro-picker--grid outro-picker--cols-2">
|
|
13
46
|
<label class="outro-picker__field">
|
|
14
|
-
|
|
15
|
-
<%= f.text_field :artist, class:
|
|
47
|
+
<%= tag.span "Artist", class: outro_label_class %>
|
|
48
|
+
<%= f.text_field :artist, class: outro_input_class %>
|
|
16
49
|
</label>
|
|
17
50
|
|
|
18
51
|
<label class="outro-picker__field">
|
|
19
|
-
|
|
20
|
-
<%= f.text_field :song, class:
|
|
52
|
+
<%= tag.span "Song", class: outro_label_class %>
|
|
53
|
+
<%= f.text_field :song, class: outro_input_class %>
|
|
21
54
|
</label>
|
|
22
55
|
</div>
|
|
23
56
|
|
|
24
57
|
<div class="outro-picker outro-picker--grid outro-picker--cols-3">
|
|
25
58
|
<label class="outro-picker__field">
|
|
26
|
-
|
|
59
|
+
<%= tag.span "Tuning", class: outro_label_class %>
|
|
27
60
|
<%= f.select :tuning, chord_chart_tuning_options, {},
|
|
28
|
-
class:
|
|
61
|
+
class: outro_select_class %>
|
|
29
62
|
</label>
|
|
30
63
|
|
|
31
64
|
<label class="outro-picker__field">
|
|
32
|
-
|
|
65
|
+
<%= tag.span "Capo", class: outro_label_class %>
|
|
33
66
|
<%= f.select :capo, chord_chart_capo_options,
|
|
34
|
-
{ include_blank: "None" }, class:
|
|
67
|
+
{ include_blank: "None" }, class: outro_select_class %>
|
|
35
68
|
</label>
|
|
36
69
|
|
|
37
70
|
<label class="outro-picker__field">
|
|
38
|
-
|
|
39
|
-
<%= f.select :key, chord_chart_key_options, {},
|
|
71
|
+
<%= tag.span "Key", class: outro_label_class %>
|
|
72
|
+
<%= f.select :key, chord_chart_key_options, {},
|
|
73
|
+
class: outro_select_class %>
|
|
40
74
|
</label>
|
|
41
75
|
</div>
|
|
42
76
|
|
|
43
77
|
<label class="outro-transposer__field">
|
|
44
|
-
|
|
78
|
+
<%= tag.span "Chords & lyrics (Optional)", class: outro_label_class %>
|
|
45
79
|
<%= f.text_area :body,
|
|
46
80
|
rows: 16,
|
|
47
81
|
placeholder: "Paste or type a chord chart here.",
|
|
48
|
-
class: "outro-
|
|
82
|
+
class: outro_textarea_class("outro-transposer__textarea") %>
|
|
49
83
|
</label>
|
|
50
84
|
</fieldset>
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<%= search_field_tag :q, params[:missing],
|
|
6
6
|
placeholder: "F#m7b5 or fsharpm7flat5",
|
|
7
7
|
"aria-label": "Go to chord by symbol or slug",
|
|
8
|
-
class: "outro-
|
|
8
|
+
class: outro_input_class("outro-filters__search-input") %>
|
|
9
9
|
<%= button_tag type: :submit, name: nil,
|
|
10
|
-
class:
|
|
10
|
+
class: outro_button_class,
|
|
11
11
|
"aria-label": "Search" do %>
|
|
12
12
|
<%= image_tag "outro_rails/search.svg", class: 'outro-button__icon' %>
|
|
13
13
|
Search
|
|
@@ -18,52 +18,52 @@
|
|
|
18
18
|
<%= form_with url: chords_path, method: :get,
|
|
19
19
|
class: "outro-picker outro-filters__facets" do %>
|
|
20
20
|
<label class="outro-picker__field">
|
|
21
|
-
|
|
21
|
+
<%= tag.span "Root", class: outro_label_class %>
|
|
22
22
|
<%= select_tag :root,
|
|
23
23
|
options_for_select(chord_root_options, @filters[:root]),
|
|
24
24
|
include_blank: "Any",
|
|
25
|
-
class:
|
|
25
|
+
class: outro_select_class,
|
|
26
26
|
onchange: "this.form.requestSubmit()" %>
|
|
27
27
|
</label>
|
|
28
28
|
|
|
29
29
|
<label class="outro-picker__field">
|
|
30
|
-
|
|
30
|
+
<%= tag.span "Quality", class: outro_label_class %>
|
|
31
31
|
<%= select_tag :quality,
|
|
32
32
|
options_for_select(chord_quality_options, @filters[:quality]),
|
|
33
33
|
include_blank: "Any",
|
|
34
|
-
class:
|
|
34
|
+
class: outro_select_class,
|
|
35
35
|
onchange: "this.form.requestSubmit()" %>
|
|
36
36
|
</label>
|
|
37
37
|
|
|
38
38
|
<label class="outro-picker__field">
|
|
39
|
-
|
|
39
|
+
<%= tag.span "Extension", class: outro_label_class %>
|
|
40
40
|
<%= select_tag :extension,
|
|
41
41
|
options_for_select(chord_extension_options, @filters[:extension]),
|
|
42
42
|
include_blank: "Any",
|
|
43
|
-
class:
|
|
43
|
+
class: outro_select_class,
|
|
44
44
|
onchange: "this.form.requestSubmit()" %>
|
|
45
45
|
</label>
|
|
46
46
|
|
|
47
47
|
<label class="outro-picker__field">
|
|
48
|
-
|
|
48
|
+
<%= tag.span "Alteration", class: outro_label_class %>
|
|
49
49
|
<%= select_tag :alteration,
|
|
50
50
|
options_for_select(chord_alteration_options, @filters[:alteration]),
|
|
51
51
|
include_blank: "Any",
|
|
52
|
-
class:
|
|
52
|
+
class: outro_select_class,
|
|
53
53
|
onchange: "this.form.requestSubmit()" %>
|
|
54
54
|
</label>
|
|
55
55
|
|
|
56
56
|
<label class="outro-picker__field">
|
|
57
|
-
|
|
57
|
+
<%= tag.span "Added tone", class: outro_label_class %>
|
|
58
58
|
<%= select_tag :added_tone,
|
|
59
59
|
options_for_select(chord_added_tone_options, @filters[:added_tone]),
|
|
60
60
|
include_blank: "Any",
|
|
61
|
-
class:
|
|
61
|
+
class: outro_select_class,
|
|
62
62
|
onchange: "this.form.requestSubmit()" %>
|
|
63
63
|
</label>
|
|
64
64
|
|
|
65
65
|
<%= button_tag type: :submit, name: nil,
|
|
66
|
-
class:
|
|
66
|
+
class: outro_button_class,
|
|
67
67
|
"aria-label": "Filter" do %>
|
|
68
68
|
<%= image_tag "outro_rails/filter.svg", class: 'outro-button__icon' %>
|
|
69
69
|
Filter
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
|
|
72
72
|
<% if filters_active? %>
|
|
73
73
|
<%= link_to chords_path,
|
|
74
|
-
class:
|
|
74
|
+
class: outro_button_class,
|
|
75
75
|
"aria-label": "Clear" do %>
|
|
76
76
|
<%= image_tag "outro_rails/clear.svg", class: 'outro-button__icon' %>
|
|
77
77
|
Clear
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<div class="outro-empty">
|
|
20
20
|
<p>No chords match those filters.</p>
|
|
21
21
|
<%= link_to chords_path,
|
|
22
|
-
class:
|
|
22
|
+
class: outro_button_class,
|
|
23
23
|
"aria-label": "Clear Filters" do %>
|
|
24
24
|
<%= image_tag "outro_rails/clear.svg", class: 'outro-button__icon' %>
|
|
25
25
|
Clear Filters
|
|
@@ -63,9 +63,8 @@
|
|
|
63
63
|
</dl>
|
|
64
64
|
|
|
65
65
|
<div class="outro-card__footer">
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
</span>
|
|
66
|
+
<%= tag.span "See this scale on", id: "scale-instruments-label",
|
|
67
|
+
class: outro_label_class %>
|
|
69
68
|
<div class="outro-chip-row" role="group"
|
|
70
69
|
aria-labelledby="scale-instruments-label">
|
|
71
70
|
<% OutroRails::ScalesController::INSTRUMENTS.each do |instrument| %>
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
<%= hidden_field_tag :instrument, @instrument %>
|
|
13
13
|
|
|
14
14
|
<label class="outro-picker__field">
|
|
15
|
-
|
|
15
|
+
<%= tag.span "Tuning", class: outro_label_class %>
|
|
16
16
|
<%= select_tag :tuning,
|
|
17
17
|
options_for_select(guitar_tuning_options, tuning.slug),
|
|
18
|
-
class:
|
|
18
|
+
class: outro_select_class,
|
|
19
19
|
onchange: "this.form.requestSubmit()" %>
|
|
20
20
|
</label>
|
|
21
21
|
|
|
22
22
|
<%# Fallback for keyboard users / no JS; the select auto-submits
|
|
23
23
|
on change. %>
|
|
24
24
|
<%= button_tag type: :submit, name: nil,
|
|
25
|
-
class:
|
|
25
|
+
class: outro_button_class,
|
|
26
26
|
"aria-label": "Show Tuning" do %>
|
|
27
27
|
<%= image_tag "outro_rails/guitar.svg", class: 'outro-button__icon' %>
|
|
28
28
|
Show Tuning
|
|
@@ -3,33 +3,33 @@
|
|
|
3
3
|
<%= hidden_field_tag :instrument, @instrument %>
|
|
4
4
|
|
|
5
5
|
<label class="outro-picker__field">
|
|
6
|
-
|
|
6
|
+
<%= tag.span "Root", class: outro_label_class %>
|
|
7
7
|
<%= select_tag :root,
|
|
8
8
|
options_for_select(scale_root_options, @scale.root.path_slug),
|
|
9
|
-
class:
|
|
9
|
+
class: outro_select_class,
|
|
10
10
|
onchange: "this.form.requestSubmit()" %>
|
|
11
11
|
</label>
|
|
12
12
|
|
|
13
13
|
<label class="outro-picker__field">
|
|
14
|
-
|
|
14
|
+
<%= tag.span "Scale", class: outro_label_class %>
|
|
15
15
|
<%= select_tag :type,
|
|
16
16
|
options_for_select(scale_type_options, @scale.slug),
|
|
17
|
-
class:
|
|
17
|
+
class: outro_select_class,
|
|
18
18
|
onchange: "this.form.requestSubmit()" %>
|
|
19
19
|
</label>
|
|
20
20
|
|
|
21
21
|
<% if @instrument == "guitar" %>
|
|
22
22
|
<label class="outro-picker__field">
|
|
23
|
-
|
|
23
|
+
<%= tag.span "Tuning", class: outro_label_class %>
|
|
24
24
|
<%= select_tag :tuning,
|
|
25
25
|
options_for_select(guitar_tuning_options, @tuning&.slug),
|
|
26
|
-
class:
|
|
26
|
+
class: outro_select_class,
|
|
27
27
|
onchange: "this.form.requestSubmit()" %>
|
|
28
28
|
</label>
|
|
29
29
|
<% end %>
|
|
30
30
|
|
|
31
31
|
<%= button_tag type: :submit, name: nil,
|
|
32
|
-
class:
|
|
32
|
+
class: outro_button_class,
|
|
33
33
|
"aria-label": "Show Scale" do %>
|
|
34
34
|
<%= image_tag "outro_rails/scales.svg", class: 'outro-button__icon' %>
|
|
35
35
|
Show Scale
|
|
@@ -77,9 +77,8 @@
|
|
|
77
77
|
<% others = OutroRails::ScalesController::INSTRUMENTS - [ @instrument ] %>
|
|
78
78
|
<% if others.any? %>
|
|
79
79
|
<div class="outro-switch">
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
</span>
|
|
80
|
+
<%= tag.span "View this scale on", id: "scale-switch-label",
|
|
81
|
+
class: outro_label_class %>
|
|
83
82
|
<div class="outro-chip-row" role="group"
|
|
84
83
|
aria-labelledby="scale-switch-label">
|
|
85
84
|
<% others.each do |other| %>
|
|
@@ -21,39 +21,39 @@
|
|
|
21
21
|
class: "outro-transposer" do %>
|
|
22
22
|
<div class="outro-picker outro-picker--flush">
|
|
23
23
|
<label class="outro-picker__field">
|
|
24
|
-
|
|
24
|
+
<%= tag.span "Convert", class: outro_label_class %>
|
|
25
25
|
<%= select_tag :mode,
|
|
26
26
|
options_for_select(transposer_mode_options, @mode),
|
|
27
|
-
class:
|
|
27
|
+
class: outro_select_class %>
|
|
28
28
|
</label>
|
|
29
29
|
|
|
30
30
|
<label class="outro-picker__field">
|
|
31
|
-
|
|
31
|
+
<%= tag.span "Original key", class: outro_label_class %>
|
|
32
32
|
<%= select_tag :from_key,
|
|
33
33
|
grouped_options_for_select(transposer_key_options,
|
|
34
34
|
@from_key.to_s),
|
|
35
|
-
class:
|
|
35
|
+
class: outro_select_class %>
|
|
36
36
|
</label>
|
|
37
37
|
|
|
38
38
|
<label class="outro-picker__field">
|
|
39
|
-
|
|
39
|
+
<%= tag.span "New key", class: outro_label_class %>
|
|
40
40
|
<%= select_tag :to_key,
|
|
41
41
|
grouped_options_for_select(transposer_key_options,
|
|
42
42
|
@to_key.to_s),
|
|
43
|
-
class:
|
|
43
|
+
class: outro_select_class %>
|
|
44
44
|
</label>
|
|
45
45
|
</div>
|
|
46
46
|
|
|
47
47
|
<label class="outro-transposer__field">
|
|
48
|
-
|
|
48
|
+
<%= tag.span "Chords and lyrics", class: outro_label_class %>
|
|
49
49
|
<%= text_area_tag :text, @text,
|
|
50
50
|
rows: 8,
|
|
51
51
|
placeholder: "Paste a chord chart here.",
|
|
52
|
-
class: "outro-
|
|
52
|
+
class: outro_textarea_class("outro-transposer__textarea") %>
|
|
53
53
|
</label>
|
|
54
54
|
|
|
55
55
|
<%= button_tag type: :submit, name: nil,
|
|
56
|
-
class:
|
|
56
|
+
class: outro_button_class,
|
|
57
57
|
"aria-label": "Transpose" do %>
|
|
58
58
|
<%= image_tag "outro_rails/transposer.svg",
|
|
59
59
|
class: 'outro-button__icon' %>
|
|
@@ -5,7 +5,7 @@ module OutroRails
|
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
7
|
class_methods do
|
|
8
|
-
def has_chord_chart(name, dependent: :destroy)
|
|
8
|
+
def has_chord_chart(name, required: true, dependent: :destroy)
|
|
9
9
|
association_name = :"chord_chart_#{name}"
|
|
10
10
|
|
|
11
11
|
has_one association_name,
|
|
@@ -14,25 +14,77 @@ module OutroRails
|
|
|
14
14
|
as: :record,
|
|
15
15
|
inverse_of: :record,
|
|
16
16
|
autosave: true,
|
|
17
|
+
validate: false,
|
|
17
18
|
dependent: dependent
|
|
18
19
|
|
|
19
20
|
accepts_nested_attributes_for association_name
|
|
20
21
|
|
|
22
|
+
validate do
|
|
23
|
+
chart = association(association_name).target
|
|
24
|
+
next if chart.nil? || chart.destroyed? || chart.marked_for_destruction?
|
|
25
|
+
next unless chart.changed_for_autosave?
|
|
26
|
+
next if chart.valid?
|
|
27
|
+
|
|
28
|
+
chart.errors.each do |error|
|
|
29
|
+
attribute = error.attribute == :base ? name : :"#{name}.#{error.attribute}"
|
|
30
|
+
errors.import(error, attribute: attribute)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
fallback = if required
|
|
35
|
+
"build_#{association_name}(name: #{name.to_s.inspect})"
|
|
36
|
+
else
|
|
37
|
+
"nil"
|
|
38
|
+
end
|
|
39
|
+
|
|
21
40
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
22
41
|
def #{name}
|
|
23
|
-
#{association_name}
|
|
24
|
-
|
|
42
|
+
chart = #{association_name}
|
|
43
|
+
return chart if chart && !chart.destroyed? &&
|
|
44
|
+
!chart.marked_for_destruction?
|
|
45
|
+
|
|
46
|
+
#{fallback}
|
|
25
47
|
end
|
|
26
48
|
|
|
27
49
|
def #{name}?
|
|
28
|
-
#{association_name}
|
|
50
|
+
chart = #{association_name}
|
|
51
|
+
!chart.nil? && !chart.destroyed? && !chart.marked_for_destruction?
|
|
29
52
|
end
|
|
30
53
|
|
|
31
54
|
def #{name}=(attributes)
|
|
32
|
-
|
|
55
|
+
if attributes.nil?
|
|
56
|
+
self.#{association_name} = nil
|
|
57
|
+
else
|
|
58
|
+
chart = #{association_name} ||
|
|
59
|
+
build_#{association_name}(name: #{name.to_s.inspect})
|
|
60
|
+
chart.assign_attributes(attributes)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def #{name}_required?
|
|
65
|
+
#{required}
|
|
33
66
|
end
|
|
34
67
|
CODE
|
|
35
68
|
|
|
69
|
+
unless required
|
|
70
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
71
|
+
def #{association_name}_attributes=(attributes)
|
|
72
|
+
unless OutroRails::ChordChart.blank_attributes?(attributes)
|
|
73
|
+
return super
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
chart = #{association_name}
|
|
77
|
+
return unless chart
|
|
78
|
+
|
|
79
|
+
if chart.persisted?
|
|
80
|
+
chart.mark_for_destruction
|
|
81
|
+
else
|
|
82
|
+
self.#{association_name} = nil
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
CODE
|
|
86
|
+
end
|
|
87
|
+
|
|
36
88
|
scope :"with_#{name}", -> { includes(association_name) }
|
|
37
89
|
end
|
|
38
90
|
end
|
data/lib/outro_rails/version.rb
CHANGED
data/lib/outro_rails.rb
CHANGED
|
@@ -18,4 +18,35 @@ module OutroRails
|
|
|
18
18
|
!!chord_chart_voicing_authorization.call(chord_chart: chord_chart,
|
|
19
19
|
controller: controller)
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
ELEMENT_CLASS_DEFAULTS = {
|
|
23
|
+
button: "outro-button",
|
|
24
|
+
input: "outro-input",
|
|
25
|
+
label: "outro-label",
|
|
26
|
+
select: "outro-select",
|
|
27
|
+
textarea: "outro-textarea"
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
mattr_accessor :button_class, default: ELEMENT_CLASS_DEFAULTS[:button]
|
|
31
|
+
mattr_accessor :input_class, default: ELEMENT_CLASS_DEFAULTS[:input]
|
|
32
|
+
mattr_accessor :label_class, default: ELEMENT_CLASS_DEFAULTS[:label]
|
|
33
|
+
mattr_accessor :select_class, default: ELEMENT_CLASS_DEFAULTS[:select]
|
|
34
|
+
mattr_accessor :textarea_class, default: ELEMENT_CLASS_DEFAULTS[:textarea]
|
|
35
|
+
|
|
36
|
+
def self.configure
|
|
37
|
+
yield self
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.class_for(element)
|
|
42
|
+
key = element.to_sym
|
|
43
|
+
|
|
44
|
+
unless ELEMENT_CLASS_DEFAULTS.key?(key)
|
|
45
|
+
raise ArgumentError,
|
|
46
|
+
"unknown element #{element.inspect}; expected one of " \
|
|
47
|
+
"#{ELEMENT_CLASS_DEFAULTS.keys.map(&:inspect).join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
public_send(:"#{key}_class")
|
|
51
|
+
end
|
|
21
52
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: outro_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- endtoendpaper
|
|
@@ -79,6 +79,7 @@ files:
|
|
|
79
79
|
- app/helpers/outro_rails/chord_charts_helper.rb
|
|
80
80
|
- app/helpers/outro_rails/chords_helper.rb
|
|
81
81
|
- app/helpers/outro_rails/circle_of_fifths_helper.rb
|
|
82
|
+
- app/helpers/outro_rails/class_names_helper.rb
|
|
82
83
|
- app/helpers/outro_rails/instruments_helper.rb
|
|
83
84
|
- app/helpers/outro_rails/scales_helper.rb
|
|
84
85
|
- app/helpers/outro_rails/transposer_helper.rb
|