tramway 3.1.2.5 → 3.1.2.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 564610d50165b34a4446cdc570c377a12f9c49587b366c994141a3e2702e289f
4
- data.tar.gz: beed0c0e6d742dbec6caeaed2fff43c0b6ff62248b804d723dc7b0ce98883b4b
3
+ metadata.gz: 1bc6a1e219c499e701dd4ec09e3e04d722ba480744c25db62f4b8f5b75f3c365
4
+ data.tar.gz: 5fd9987b47419d936e0bd50dbf727147c149c33948188d53589cc3666482829b
5
5
  SHA512:
6
- metadata.gz: 98bfc099a4338a4e114a7591d9721b209077e3a419b83669b5c5fbe5ce21a4c004515a97af9d4a32bf3c5219c426198176c5e413b3633569b68935f623eb9f3f
7
- data.tar.gz: f7d43d03755310fa2ae39e8d0a89c2c87ff90791843ce7ee899eae5ef518dd06b7830c45a0a861159dcf7378862c3e008ef7411b21b465a19ade3863085880a1
6
+ metadata.gz: 7c4fb50d8f6579be7bb697af7a93b3e1b69199724e6ca23b1a54e03809bc8de896a43d2faf22dcfd0bc3f1755648689c888659c0ffcb19f90407a2036a2c0d44
7
+ data.tar.gz: cc2131f3a6f9302ab31db63ca04fce6339e770c9694a97d147e340e8294002ed1707bd5ed5c511b2e1293f369f23139bd57e6236d467fcc9a7f84b8bc0e00e68
data/README.md CHANGED
@@ -535,6 +535,86 @@ class UserDecorator < Tramway::BaseDecorator
535
535
  end
536
536
  ```
537
537
 
538
+ #### Show associations
539
+
540
+ Entity show pages can render associated records by returning association names from
541
+ the decorator's `show_associations` method. Each name must be an actual
542
+ Active Record association on the decorated model; Tramway raises a clear error
543
+ when the association is missing.
544
+
545
+ *app/models/post.rb*
546
+ ```ruby
547
+ class Post < ApplicationRecord
548
+ has_many :comments
549
+ end
550
+ ```
551
+
552
+ *app/decorators/post_decorator.rb*
553
+ ```ruby
554
+ class PostDecorator < Tramway::BaseDecorator
555
+ association :comments
556
+
557
+ def show_associations
558
+ %i[comments]
559
+ end
560
+ end
561
+ ```
562
+
563
+ Tramway renders a `New` button for a shown association when it can determine a
564
+ create path for that association. Define the associated model as a Tramway entity
565
+ with a `:create` page to let Tramway build that path automatically:
566
+
567
+ *config/initializers/tramway.rb*
568
+ ```ruby
569
+ Tramway.configure do |config|
570
+ config.entities = [
571
+ {
572
+ name: :post,
573
+ pages: [
574
+ { action: :show }
575
+ ]
576
+ },
577
+ {
578
+ name: :comment,
579
+ pages: [
580
+ { action: :create }
581
+ ]
582
+ }
583
+ ]
584
+ end
585
+ ```
586
+
587
+ When `new_record_path` is omitted, Tramway looks for a configured entity using
588
+ the association reflection's class name in underscore format. This means aliases
589
+ such as `has_many :responses, class_name: 'Comment'` use the `comment` entity,
590
+ not a `response` entity.
591
+
592
+ Generated association create paths include a `redirect` query parameter pointing
593
+ to the current record's configured Tramway entity show path, for example:
594
+ `/comments/new?redirect=%2Fposts%2F1`.
595
+
596
+ You can override the path explicitly when the associated record should be created
597
+ through a custom route:
598
+
599
+ ```ruby
600
+ class PostDecorator < Tramway::BaseDecorator
601
+ association :comments
602
+
603
+ def show_associations
604
+ [
605
+ [
606
+ :comments,
607
+ { new_record_path: Rails.application.routes.url_helpers.new_post_comment_path(object) }
608
+ ]
609
+ ]
610
+ end
611
+ end
612
+ ```
613
+
614
+ Tramway still appends the `redirect` query parameter to custom paths. Associations
615
+ without a create route and without `new_record_path` still render their records,
616
+ but no creation button is displayed.
617
+
538
618
  #### Decorate nil
539
619
 
540
620
  Tramway Decorator does not decorate nil objects
@@ -1230,7 +1310,7 @@ Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are
1230
1310
 
1231
1311
  Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by default. Form inputs use hardcoded
1232
1312
  dark shadcn-style classes; Tramway does not render a separate light form theme.
1233
- Checkboxes render with a visible, fixed-size dark unchecked surface and use the light primary checked state.
1313
+ Checkboxes render with the same dark border and focus highlight treatment as other inputs, then use the light primary checked state.
1234
1314
  Checkbox controls, checkmarks, and vertically centered label line heights scale together with the form-level `size:` option.
1235
1315
 
1236
1316
  ```erb
@@ -1253,10 +1333,12 @@ will render [this](https://play.tailwindcss.com/xho3LfjKkK)
1253
1333
  Use `size:` to control the input sizing (`:small`, `:medium`, or `:large`). The default is `:medium`, and supported inputs
1254
1334
  rendered within the form will use the same size value.
1255
1335
  Date and datetime fields open the browser's native picker when clicking anywhere in the input, not only the picker icon.
1336
+ Use `hint:` on any field to render muted helper text below the input. The hint option is handled by Tramway and is not
1337
+ forwarded as an HTML attribute.
1256
1338
 
1257
1339
  ```erb
1258
1340
  <%= tramway_form_for @user, size: :large do |f| %>
1259
- <%= f.text_field :text %>
1341
+ <%= f.text_field :text, hint: 'This name is visible to teammates.' %>
1260
1342
  <%= f.submit 'Create User' %>
1261
1343
  <% end %>
1262
1344
  ```
@@ -389,12 +389,12 @@ class UiCheckbox extends Controller {
389
389
  this.buttonTarget.setAttribute("aria-checked", checked.toString())
390
390
  this.buttonTarget.dataset.state = state
391
391
  this.syncBoxStyle(checked)
392
- this.buttonTarget.classList.add("border-zinc-50")
392
+ this.buttonTarget.classList.toggle("border-zinc-50", checked)
393
+ this.buttonTarget.classList.toggle("border-zinc-800", !checked)
393
394
  this.buttonTarget.classList.toggle("bg-zinc-50", checked)
394
395
  this.buttonTarget.classList.toggle("text-zinc-950", checked)
395
- this.buttonTarget.classList.toggle("bg-zinc-900", !checked)
396
+ this.buttonTarget.classList.toggle("bg-zinc-950", !checked)
396
397
  this.buttonTarget.classList.toggle("text-zinc-50", !checked)
397
- this.buttonTarget.classList.remove("border-zinc-800", "bg-zinc-950")
398
398
  this.indicatorTarget.classList.toggle("hidden", !checked)
399
399
  this.buttonTarget.toggleAttribute("disabled", this.inputTarget.disabled)
400
400
  }
@@ -412,10 +412,8 @@ class UiCheckbox extends Controller {
412
412
  this.buttonTarget.style.padding = "0"
413
413
  this.buttonTarget.style.lineHeight = "1"
414
414
  this.buttonTarget.style.boxSizing = "border-box"
415
- this.buttonTarget.style.border = "1px solid #fafafa"
416
- this.buttonTarget.style.backgroundColor = checked ? "#fafafa" : "#18181b"
415
+ this.buttonTarget.style.backgroundColor = checked ? "#fafafa" : "#09090b"
417
416
  this.buttonTarget.style.color = checked ? "#09090b" : "#fafafa"
418
- this.buttonTarget.style.boxShadow = "0 0 0 1px #fafafa"
419
417
  }
420
418
 
421
419
  checkboxSize() {
@@ -10,6 +10,7 @@ class TailwindComponent < Tramway::BaseComponent
10
10
  option :options
11
11
  option :label
12
12
  option :for
13
+ option :hint, optional: true, default: -> {}
13
14
  option :size, default: -> { :medium }
14
15
 
15
16
  SIZE_CLASSES = {
@@ -72,7 +73,7 @@ class TailwindComponent < Tramway::BaseComponent
72
73
  end
73
74
 
74
75
  def checkbox_base_classes
75
- 'shrink-0 rounded-sm border border-zinc-800 bg-zinc-950 text-zinc-50 shadow-sm ' \
76
+ 'shrink-0 rounded-sm border border-zinc-800 bg-zinc-950 text-zinc-50 shadow-sm transition-colors ' \
76
77
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-300 ' \
77
78
  'focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950 disabled:cursor-not-allowed ' \
78
79
  'disabled:opacity-50'
@@ -83,6 +84,10 @@ class TailwindComponent < Tramway::BaseComponent
83
84
  'peer-disabled:opacity-70'
84
85
  end
85
86
 
87
+ def form_hint_classes
88
+ 'mt-1 text-sm text-zinc-400'
89
+ end
90
+
86
91
  def default_container_classes
87
92
  return if options[:horizontal]
88
93
 
@@ -210,11 +210,16 @@ module Tramway
210
210
  attribute:,
211
211
  label: label_build(attribute, options),
212
212
  for: options[:id].presence || for_id(attribute),
213
+ hint: hint_option(options),
213
214
  options: options,
214
215
  size: form_size
215
216
  }
216
217
  end
217
218
 
219
+ def hint_option(options)
220
+ options.delete(:hint) || options.delete('hint')
221
+ end
222
+
218
223
  def label_build(attribute, options)
219
224
  label_option = options[:label]
220
225
 
@@ -1,29 +1,32 @@
1
- .flex.items-center.gap-2{ class: default_container_classes, data: { controller: 'ui--checkbox' } }
2
- - checkbox_checked = checked?
3
- = @input.call @attribute, **hidden_checkbox_options
4
- %button{ type: 'button',
5
- role: 'checkbox',
6
- 'aria-checked' => checkbox_checked.to_s,
7
- 'data-action' => 'click->ui--checkbox#toggle',
8
- 'data-state' => checkbox_checked ? 'checked' : 'unchecked',
9
- 'data-ui--checkbox-target' => 'button',
10
- value: 'on',
11
- class: checkbox_button_classes,
12
- style: checkbox_button_style,
13
- for: @for,
14
- id: @for }
15
- %span{ class: checkbox_indicator_classes, style: 'pointer-events: none', data: { 'ui--checkbox-target' => 'indicator' } }
16
- %svg{ xmlns: 'http://www.w3.org/2000/svg',
17
- width: '24',
18
- height: '24',
19
- viewBox: '0 0 24 24',
20
- fill: 'none',
21
- stroke: 'currentColor',
22
- 'stroke-width' => '2',
23
- 'stroke-linecap' => 'round',
24
- 'stroke-linejoin' => 'round',
25
- class: size_class(:checkbox_indicator) }
26
- %polyline{ points: '20 6 9 17 4 12' }
27
- - if @label
28
- = component('tramway/form/label', for: @for, options: { class: label_classes, style: label_style, data: { action: 'click->ui--checkbox#toggle' } }) do
29
- = @label
1
+ %div{ class: default_container_classes, data: { controller: 'ui--checkbox' } }
2
+ .flex.items-center.gap-2
3
+ - checkbox_checked = checked?
4
+ = @input.call @attribute, **hidden_checkbox_options
5
+ %button{ type: 'button',
6
+ role: 'checkbox',
7
+ 'aria-checked' => checkbox_checked.to_s,
8
+ 'data-action' => 'click->ui--checkbox#toggle',
9
+ 'data-state' => checkbox_checked ? 'checked' : 'unchecked',
10
+ 'data-ui--checkbox-target' => 'button',
11
+ value: 'on',
12
+ class: checkbox_button_classes,
13
+ style: checkbox_button_style,
14
+ for: @for,
15
+ id: @for }
16
+ %span{ class: checkbox_indicator_classes, style: 'pointer-events: none', data: { 'ui--checkbox-target' => 'indicator' } }
17
+ %svg{ xmlns: 'http://www.w3.org/2000/svg',
18
+ width: '24',
19
+ height: '24',
20
+ viewBox: '0 0 24 24',
21
+ fill: 'none',
22
+ stroke: 'currentColor',
23
+ 'stroke-width' => '2',
24
+ 'stroke-linecap' => 'round',
25
+ 'stroke-linejoin' => 'round',
26
+ class: size_class(:checkbox_indicator) }
27
+ %polyline{ points: '20 6 9 17 4 12' }
28
+ - if @label
29
+ = component('tramway/form/label', for: @for, options: { class: label_classes, style: label_style, data: { action: 'click->ui--checkbox#toggle' } }) do
30
+ = @label
31
+ - if @hint.present?
32
+ %p{ class: form_hint_classes }= @hint
@@ -10,9 +10,7 @@ module Tramway
10
10
  'justify-content': 'center',
11
11
  padding: '0',
12
12
  'line-height': '1',
13
- 'box-sizing': 'border-box',
14
- border: '1px solid #fafafa',
15
- 'box-shadow': '0 0 0 1px #fafafa'
13
+ 'box-sizing': 'border-box'
16
14
  }.freeze
17
15
 
18
16
  CHECKBOX_BUTTON_SIZES = {
@@ -22,10 +20,7 @@ module Tramway
22
20
  }.freeze
23
21
 
24
22
  def checkbox_button_classes
25
- "peer #{size_class(:checkbox_input)} shrink-0 rounded-sm border border-zinc-50 bg-zinc-900 text-zinc-50 " \
26
- 'ring-offset-zinc-950 ' \
27
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-300 ' \
28
- 'focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ' \
23
+ "peer #{size_class(:checkbox_input)} #{checkbox_base_classes} " \
29
24
  'data-[state=checked]:border-zinc-50 data-[state=checked]:bg-zinc-50 ' \
30
25
  'data-[state=checked]:text-zinc-950'
31
26
  end
@@ -87,7 +82,7 @@ module Tramway
87
82
  height: checkbox_button_size,
88
83
  'min-width': checkbox_button_size,
89
84
  'min-height': checkbox_button_size,
90
- 'background-color': checked? ? '#fafafa' : '#18181b',
85
+ 'background-color': checked? ? '#fafafa' : '#09090b',
91
86
  color: checked? ? '#09090b' : '#fafafa'
92
87
  )
93
88
  end
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **picker_options(classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **picker_options(classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -5,3 +5,5 @@
5
5
  %label{ for: @for, class: classes }
6
6
  = @label
7
7
  = @input
8
+ - if @hint.present?
9
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **@options.merge(class: classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -12,3 +12,5 @@
12
12
  = component('tramway/form/label', for: @for, class: 'mb-0') do
13
13
  = @label
14
14
  = @input.call @attribute, **rich_text_area_options
15
+ - if @hint.present?
16
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:select_input)} #{select_base_classes}"
6
6
  = @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: classes))
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **@options.merge(class: classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **@options.merge(class: classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -4,3 +4,5 @@
4
4
  = @label
5
5
  - classes = "#{size_class(:text_input)} #{text_input_base_classes}"
6
6
  = @input.call @attribute, **@options.merge(class: classes), value: @value
7
+ - if @hint.present?
8
+ %p{ class: form_hint_classes }= @hint
@@ -11,3 +11,5 @@
11
11
  = component 'tramway/form/tramway_select/caret', size:, direction: :down
12
12
  .caret-up.hidden{ data: { "tramway-select-target" => "caretUp" } }
13
13
  = component 'tramway/form/tramway_select/caret', size:, direction: :up
14
+ - if @hint.present?
15
+ %p{ class: form_hint_classes }= @hint
@@ -91,35 +91,7 @@ module Tramway
91
91
  end
92
92
 
93
93
  def set_associations
94
- @associations = @record.show_associations.map do |(association, options)|
95
- options ||= {}
96
- association_type = @record.object.class.reflect_on_association(association).macro
97
-
98
- {
99
- name: association,
100
- association_type:,
101
- **send("#{association_type}_associations", association, options)
102
- }
103
- end.compact
104
- end
105
-
106
- # rubocop:disable Naming/PredicatePrefix
107
- def has_many_associations(association, options)
108
- records = Kaminari.paginate_array(@record.public_send(association.name)).page(params[:page])
109
-
110
- {
111
- decorator: records.first&.class,
112
- records:,
113
- model_class: records.first&.object&.class,
114
- new_record_path: options[:new_record_path]
115
- }
116
- end
117
- # rubocop:enable Naming/PredicatePrefix
118
-
119
- def belongs_to_associations(association, _options)
120
- record = @record.public_send(association.name)
121
-
122
- { decorator: record.class, record:, model_class: record.class }
94
+ @associations = @record.send(:__show_associations, params[:page])
123
95
  end
124
96
 
125
97
  def search(entities)
@@ -52,9 +52,10 @@
52
52
  custom_path_method: @entity.index_helper_method,
53
53
  custom_path_arguments: [@record.id]
54
54
 
55
- = tramway_button text: t('tramway.actions.new'),
56
- path: association[:new_record_path],
57
- type: :will
55
+ - if association[:new_record_path].present?
56
+ = tramway_button text: t('tramway.actions.new'),
57
+ path: association[:new_record_path],
58
+ type: :will
58
59
 
59
60
  - if association[:records].any?
60
61
  = tramway_table class: 'mt-4' do
data/docs/AGENTS.md CHANGED
@@ -712,12 +712,15 @@ end
712
712
 
713
713
  ```ruby
714
714
  = tramway_form_for @user do |f|
715
- = f.email_field :email
715
+ = f.email_field :email, hint: 'Use the address where you receive account notifications.'
716
716
  = f.password_field :password
717
717
  = f.select :role, [["Admin", "admin"], ["Manager", "manager"]], include_blank: "Select role"
718
718
  = f.submit 'Save'
719
719
  ```
720
720
 
721
+ Use `hint:` on fields when short helper text should appear below the input. Tramway renders the hint separately and does
722
+ not forward it to the input as an HTML attribute.
723
+
721
724
  Autocomplete select example:
722
725
 
723
726
  ```ruby
@@ -3,6 +3,7 @@
3
3
  require 'tramway/decorators/name_builder'
4
4
  require 'tramway/decorators/association'
5
5
  require 'tramway/decorators/collection_decorator'
6
+ require 'tramway/decorators/show_associations'
6
7
  require 'tramway/helpers/decorate_helper'
7
8
  require 'tramway/helpers/component_helper'
8
9
  require 'tramway/helpers/views_helper'
@@ -14,6 +15,7 @@ module Tramway
14
15
  #
15
16
  class BaseDecorator
16
17
  include Tramway::Decorators::CollectionDecorators
18
+ include Tramway::Decorators::ShowAssociations
17
19
  include Tramway::Utils::Render
18
20
  include Tramway::DuckTyping::ActiveRecordCompatibility
19
21
  include Tramway::Helpers::DecorateHelper
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/utils'
4
+
5
+ module Tramway
6
+ module Decorators
7
+ # Builds metadata for associations rendered on Tramway entity show pages.
8
+ module ShowAssociations
9
+ private
10
+
11
+ def __show_associations(page)
12
+ show_associations.map do |(association, options)|
13
+ __show_association(association, options || {}, page)
14
+ end.compact
15
+ end
16
+
17
+ def __show_association(association, options, page)
18
+ reflection = __reflection_for_show_association(association)
19
+ association_type = reflection.macro
20
+ association_options = __association_options(association, association_type, options, page)
21
+ new_record_path = __association_new_record_path(reflection, association_options.delete(:new_record_path))
22
+
23
+ {
24
+ name: association,
25
+ association_type:,
26
+ new_record_path:,
27
+ **association_options
28
+ }
29
+ end
30
+
31
+ def __reflection_for_show_association(association)
32
+ reflection = object.class.reflect_on_association(association)
33
+
34
+ return reflection if reflection.present?
35
+
36
+ raise "You must define #{association} association in the #{object.class}"
37
+ end
38
+
39
+ def __association_options(association, association_type, options, page)
40
+ send("__#{association_type}_associations", association, options, page)
41
+ end
42
+
43
+ def __association_new_record_path(reflection, new_record_path)
44
+ new_record_path = __inferred_association_new_record_path(reflection) if new_record_path.blank?
45
+
46
+ return if new_record_path.blank?
47
+
48
+ __new_record_path_with_redirect(new_record_path)
49
+ end
50
+
51
+ def __inferred_association_new_record_path(reflection)
52
+ association_entity = __entity_for_name(reflection.class_name.underscore)
53
+
54
+ return if association_entity.blank? || association_entity.page(:create).blank?
55
+
56
+ Tramway::Engine.routes.url_helpers.public_send(association_entity.new_helper_method)
57
+ end
58
+
59
+ def __new_record_path_with_redirect(new_record_path)
60
+ redirect_path = __entity_show_path
61
+
62
+ return new_record_path if redirect_path.blank?
63
+
64
+ separator = new_record_path.include?('?') ? '&' : '?'
65
+
66
+ "#{new_record_path}#{separator}#{Rack::Utils.build_query(redirect: redirect_path)}"
67
+ end
68
+
69
+ def __entity_show_path
70
+ entity = __entity_for_name(object.class.name.underscore)
71
+
72
+ return if entity.blank? || entity.page(:show).blank?
73
+
74
+ Tramway::Engine.routes.url_helpers.public_send(entity.show_helper_method, object.id)
75
+ end
76
+
77
+ def __entity_for_name(name)
78
+ Tramway.config.entities.find do |config_entity|
79
+ config_entity.name == name
80
+ end
81
+ end
82
+
83
+ def __has_many_associations(association, options, page)
84
+ records = Kaminari.paginate_array(public_send(association.name)).page(page)
85
+
86
+ {
87
+ decorator: records.first&.class,
88
+ records:,
89
+ model_class: records.first&.object&.class,
90
+ new_record_path: options[:new_record_path]
91
+ }
92
+ end
93
+
94
+ def __belongs_to_associations(association, _options, _page)
95
+ record = public_send(association.name)
96
+
97
+ { decorator: record.class, record:, model_class: record.class }
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '3.1.2.5'
4
+ VERSION = '3.1.2.7'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tramway
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2.5
4
+ version: 3.1.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - kalashnikovisme
@@ -282,6 +282,7 @@ files:
282
282
  - lib/tramway/decorators/class_helper.rb
283
283
  - lib/tramway/decorators/collection_decorator.rb
284
284
  - lib/tramway/decorators/name_builder.rb
285
+ - lib/tramway/decorators/show_associations.rb
285
286
  - lib/tramway/duck_typing.rb
286
287
  - lib/tramway/duck_typing/active_record_compatibility.rb
287
288
  - lib/tramway/engine.rb