tramway 0.5.3 → 0.5.4
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 +25 -0
- data/app/components/tailwinds/form/select_component.html.haml +1 -1
- data/app/components/tailwinds/table/header_component.html.haml +1 -1
- data/app/components/tailwinds/table/row_component.rb +4 -2
- data/app/components/tailwinds/table_component.html.haml +2 -1
- data/app/controllers/tramway/entities_controller.rb +10 -1
- data/app/views/tramway/entities/_entity.html.haml +3 -3
- data/app/views/tramway/entities/_list.html.haml +1 -1
- data/config/routes.rb +16 -15
- data/config/tailwind.config.js +3 -0
- data/lib/tramway/configs/entities/page.rb +14 -0
- data/lib/tramway/configs/entity.rb +11 -4
- data/lib/tramway/decorators/association.rb +13 -7
- data/lib/tramway/decorators/class_helper.rb +11 -7
- data/lib/tramway/forms/properties.rb +17 -1
- data/lib/tramway/helpers/decorate_helper.rb +2 -2
- data/lib/tramway/helpers/navbar_helper.rb +4 -4
- data/lib/tramway/navbar.rb +6 -4
- data/lib/tramway/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3edafce1c744c1e57499e5682ee75f749dbf5bdefd1a9137d70ccaa95f1381f
|
4
|
+
data.tar.gz: fbb70ec105b76caad025797238eb09a7e21d5892e90be6b77c1c2858f301c666
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4deef971e492a823f04475bc6e915193735844521f8ae65ba8f07da1b1b0edb40e35d944000b648e0c36f1e33d8b2e770b6b62117f52df1a81443fb034132032
|
7
|
+
data.tar.gz: fcbea51c9f88495f24110ccac4f473a3a76f2583621fb952ec849fc77c690ee081bcba7a4e789c04f1c77bda02c41dac2d607f7c9b5a6572cdd03172cf31ac0c
|
data/README.md
CHANGED
@@ -8,6 +8,7 @@ Unite Ruby on Rails brilliance. Streamline development with Tramway.
|
|
8
8
|
* [Tramway Decorators](https://github.com/Purple-Magic/tramway#tramway-decorators)
|
9
9
|
* [Tramway Form](https://github.com/Purple-Magic/tramway#tramway-form)
|
10
10
|
* [Tramway Navbar](https://github.com/Purple-Magic/tramway#tramway-navbar)
|
11
|
+
* [Tramway Table Component](https://github.com/Purple-Magic/tramway#tramway-table-component)
|
11
12
|
* [Tailwind-styled forms](https://github.com/Purple-Magic/tramway#tailwind-styled-forms)
|
12
13
|
* [Stimulus-based inputs](https://github.com/Purple-Magic/tramway#stimulus-based-inputs)
|
13
14
|
* [Tailwind-styled pagination](https://github.com/Purple-Magic/tramway?tab=readme-ov-file#tailwind-styled-pagination-for-kaminari)
|
@@ -194,6 +195,8 @@ end
|
|
194
195
|
|
195
196
|
#### Decorate associations
|
196
197
|
|
198
|
+
**Decorate single association**
|
199
|
+
|
197
200
|
```ruby
|
198
201
|
class UserDecorator < Tramway::BaseDecorator
|
199
202
|
association :posts
|
@@ -203,6 +206,14 @@ user = tramway_decorate User.first
|
|
203
206
|
user.posts # => decorated collection of posts with PostDecorator
|
204
207
|
```
|
205
208
|
|
209
|
+
**Decorate multiple associations**
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
class UserDecorator < Tramway::BaseDecorator
|
213
|
+
associations :posts, :users
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
206
217
|
#### Decorate nil
|
207
218
|
|
208
219
|
Tramway Decorator does not decorate nil objects
|
@@ -491,6 +502,20 @@ tramway_navbar title: 'Purple Magic' do |nav|
|
|
491
502
|
end
|
492
503
|
```
|
493
504
|
|
505
|
+
### Tramway Table Component
|
506
|
+
|
507
|
+
Tramway provides a responsive, tailwind-styled table with light and dark themes.
|
508
|
+
|
509
|
+
```haml
|
510
|
+
= component 'tailwinds/table' do
|
511
|
+
= component 'tailwinds/table/header', headers: ['Column 1', 'Column 2']
|
512
|
+
= component 'tailwinds/table/row' do
|
513
|
+
= component 'tailwinds/table/cell' do
|
514
|
+
Something
|
515
|
+
= component 'tailwinds/table/cell' do
|
516
|
+
Another
|
517
|
+
```
|
518
|
+
|
494
519
|
### Tailwind-styled forms
|
495
520
|
|
496
521
|
Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are implemented with [ViewComponent](https://github.com/viewcomponent/view_component).
|
@@ -2,4 +2,4 @@
|
|
2
2
|
- if @label
|
3
3
|
= component('tailwinds/form/label', for: @for) do
|
4
4
|
= @label
|
5
|
-
= @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent py-2 px-3'))
|
5
|
+
= @input.call(@attribute, @collection, { selected: @value }, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent py-2 px-3 disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed'))
|
@@ -1,4 +1,4 @@
|
|
1
|
-
.div-table-row.
|
1
|
+
.div-table-row.grid.text-white.text-small.gap-4.bg-purple-700.dark:bg-gray-700.dark:text-gray-400{ class: "grid-cols-#{headers.count}", aria: { label: "Table Header" }, role: "row" }
|
2
2
|
- if headers.any?
|
3
3
|
- headers.each do |header|
|
4
4
|
.div-table-cell.py-4.px-6
|
@@ -9,14 +9,16 @@ module Tailwinds
|
|
9
9
|
option :options, optional: true, default: -> { {} }
|
10
10
|
|
11
11
|
def row_tag(**options, &)
|
12
|
+
default_attributes = { role: :row }
|
13
|
+
|
12
14
|
if href.present?
|
13
15
|
klass = "#{options[:class] || ''} #{link_row_classes}"
|
14
16
|
|
15
|
-
link_to(href, options.merge(class: klass)) do
|
17
|
+
link_to(href, options.merge(class: klass, **default_attributes)) do
|
16
18
|
yield if block_given?
|
17
19
|
end
|
18
20
|
else
|
19
|
-
tag.div(**options) do
|
21
|
+
tag.div(**options.merge(default_attributes)) do
|
20
22
|
yield if block_given?
|
21
23
|
end
|
22
24
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
= helpers.component 'tailwinds/table/row/preview'
|
2
2
|
|
3
|
-
|
3
|
+
- width_class = options[:class]&.include?('w-') ? '' : 'w-full'
|
4
|
+
.div-table.text-left.rtl:text-right.text-gray-500.dark:text-gray-400.mt-4{ class: "#{options[:class]} #{width_class}", **options.except(:class) }
|
4
5
|
= content
|
@@ -11,7 +11,12 @@ module Tramway
|
|
11
11
|
include Rails.application.routes.url_helpers
|
12
12
|
|
13
13
|
def index
|
14
|
-
@entities =
|
14
|
+
@entities = if entity.page(:index).scope.present?
|
15
|
+
model_class.public_send(entity.page(:index).scope)
|
16
|
+
else
|
17
|
+
model_class.order(id: :desc)
|
18
|
+
end.page(params[:page])
|
19
|
+
@namespace = entity.route.namespace
|
15
20
|
end
|
16
21
|
|
17
22
|
private
|
@@ -19,5 +24,9 @@ module Tramway
|
|
19
24
|
def model_class
|
20
25
|
@model_class ||= params[:entity][:name].classify.constantize
|
21
26
|
end
|
27
|
+
|
28
|
+
def entity
|
29
|
+
@entity ||= Tramway.config.entities.find { |e| e.name == params[:entity][:name] }
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
- decorator =
|
2
|
-
- decorated_object = decorator.decorate(entity)
|
3
|
-
= component 'tailwinds/table/row', cells: decorator.list_attributes.reduce({}) { |hash, attribute| hash.merge! attribute => decorated_object.public_send(attribute) }, href: decorated_object.show_path
|
1
|
+
- decorator = decorator_class_name(entity, @namespace)
|
2
|
+
- decorated_object = decorator.constantize.decorate(entity)
|
3
|
+
= component 'tailwinds/table/row', cells: decorator.constantize.list_attributes.reduce({}) { |hash, attribute| hash.merge! attribute => decorated_object.public_send(attribute) }, href: decorated_object.show_path
|
data/config/routes.rb
CHANGED
@@ -2,29 +2,30 @@
|
|
2
2
|
|
3
3
|
Tramway::Engine.routes.draw do
|
4
4
|
Tramway.config.entities.each do |entity|
|
5
|
-
|
5
|
+
segments = entity.name.split('/')
|
6
|
+
resource_name = segments.pop
|
7
|
+
|
8
|
+
define_resource = proc do
|
6
9
|
resources resource_name.pluralize.to_sym,
|
7
|
-
only:
|
8
|
-
controller:
|
9
|
-
defaults:
|
10
|
+
only: [:index],
|
11
|
+
controller:'/tramway/entities',
|
12
|
+
defaults: { entity: entity }
|
10
13
|
end
|
11
14
|
|
12
|
-
if
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
namespace
|
17
|
-
if
|
18
|
-
|
15
|
+
if segments.empty?
|
16
|
+
define_resource.call
|
17
|
+
else
|
18
|
+
nest = lambda do |names|
|
19
|
+
namespace names.first.to_sym do
|
20
|
+
if names.size > 1
|
21
|
+
nest.call(names.drop(1))
|
19
22
|
else
|
20
|
-
|
23
|
+
define_resource.call
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
24
27
|
|
25
|
-
|
26
|
-
else
|
27
|
-
define_resource.call(entity.name, entity)
|
28
|
+
nest.call(segments)
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
data/config/tailwind.config.js
CHANGED
@@ -50,6 +50,9 @@ module.exports = {
|
|
50
50
|
'space-x-1',
|
51
51
|
'justify-end',
|
52
52
|
'mt-2',
|
53
|
+
'disabled:bg-gray-100',
|
54
|
+
'disabled:text-gray-400',
|
55
|
+
'disabled:cursor-not-allowed',
|
53
56
|
// pagination
|
54
57
|
'bg-white', 'rounded-md', 'hover:bg-purple-100', 'dark:text-white', 'dark:bg-gray-800', 'dark:hover:bg-gray-700',
|
55
58
|
// multiselect styles
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tramway
|
4
|
+
module Configs
|
5
|
+
module Entities
|
6
|
+
# Route struct describes rules for route management
|
7
|
+
#
|
8
|
+
class Page < Dry::Struct
|
9
|
+
attribute :action, Types::Coercible::String
|
10
|
+
attribute? :scope, Types::Coercible::String
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'tramway/configs/entities/route'
|
4
|
+
require 'tramway/configs/entities/page'
|
4
5
|
|
5
6
|
module Tramway
|
6
7
|
module Configs
|
7
8
|
# Tramway is an entity-based framework
|
8
9
|
class Entity < Dry::Struct
|
9
10
|
attribute :name, Types::Coercible::String
|
10
|
-
attribute? :pages, Types::Array.of(
|
11
|
+
attribute? :pages, Types::Array.of(Tramway::Configs::Entities::Page).default([].freeze)
|
11
12
|
attribute? :route, Tramway::Configs::Entities::Route
|
12
13
|
|
13
14
|
# Route Struct contains implemented in Tramway CRUD and helpful routes for the entity
|
14
15
|
RouteStruct = Struct.new(:index)
|
15
16
|
|
16
|
-
#
|
17
|
+
# HumanName Struct contains human names forms for the entity
|
17
18
|
HumanNameStruct = Struct.new(:single, :plural)
|
18
19
|
|
19
20
|
def routes
|
@@ -31,6 +32,10 @@ module Tramway
|
|
31
32
|
HumanNameStruct.new(single, plural)
|
32
33
|
end
|
33
34
|
|
35
|
+
def page(name)
|
36
|
+
pages.find { |page| page.action == name.to_s }
|
37
|
+
end
|
38
|
+
|
34
39
|
private
|
35
40
|
|
36
41
|
def pluralized(model_name)
|
@@ -48,7 +53,7 @@ module Tramway
|
|
48
53
|
def route_helper_method
|
49
54
|
underscored_name = name.parameterize.pluralize.underscore
|
50
55
|
|
51
|
-
method_name = if
|
56
|
+
method_name = if set_page?(:index) || route.blank?
|
52
57
|
"#{underscored_name}_path"
|
53
58
|
else
|
54
59
|
route.helper_method_by(underscored_name)
|
@@ -58,8 +63,10 @@ module Tramway
|
|
58
63
|
end
|
59
64
|
|
60
65
|
def route_helper_engine
|
61
|
-
|
66
|
+
set_page?(:index) ? Tramway::Engine : Rails.application
|
62
67
|
end
|
68
|
+
|
69
|
+
alias set_page? page
|
63
70
|
end
|
64
71
|
end
|
65
72
|
end
|
@@ -22,14 +22,14 @@ module Tramway
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# has_and_belongs_to_many is not supported for now
|
25
|
-
def association(association)
|
25
|
+
def association(association, decorator: nil)
|
26
26
|
define_method(association) do
|
27
27
|
assoc = object.send(association)
|
28
28
|
|
29
29
|
if assoc.is_a?(ActiveRecord::Relation)
|
30
|
-
AssocDecoratorHelper.decorate_has_many_association assoc
|
30
|
+
AssocDecoratorHelper.decorate_has_many_association assoc, decorator_class: decorator
|
31
31
|
elsif assoc.present?
|
32
|
-
AssocDecoratorHelper.decorate_associated_object(assoc)
|
32
|
+
AssocDecoratorHelper.decorate_associated_object(assoc, decorator_class: decorator)
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
@@ -38,12 +38,18 @@ module Tramway
|
|
38
38
|
# Helper module for association decorators
|
39
39
|
module AssocDecoratorHelper
|
40
40
|
class << self
|
41
|
-
def decorate_has_many_association(assoc)
|
42
|
-
|
41
|
+
def decorate_has_many_association(assoc, decorator_class: nil)
|
42
|
+
return [] if assoc.empty?
|
43
|
+
|
44
|
+
decorator_class ||= decorator(assoc.klass)
|
45
|
+
|
46
|
+
decorator_class.decorate(assoc)
|
43
47
|
end
|
44
48
|
|
45
|
-
def decorate_associated_object(assoc)
|
46
|
-
decorator(assoc.class)
|
49
|
+
def decorate_associated_object(assoc, decorator_class: nil)
|
50
|
+
decorator_class ||= decorator(assoc.class)
|
51
|
+
|
52
|
+
decorator_class.decorate(assoc)
|
47
53
|
end
|
48
54
|
|
49
55
|
def decorator(class_name)
|
@@ -6,27 +6,31 @@ module Tramway
|
|
6
6
|
module ClassHelper
|
7
7
|
module_function
|
8
8
|
|
9
|
-
def decorator_class(object_or_array, decorator = nil)
|
9
|
+
def decorator_class(object_or_array, decorator = nil, namespace = nil)
|
10
10
|
raise_error_if_object_empty object_or_array, decorator
|
11
11
|
|
12
12
|
return decorator if decorator.present?
|
13
13
|
|
14
14
|
begin
|
15
|
-
class_name = decorator_class_name(object_or_array)
|
15
|
+
class_name = decorator_class_name(object_or_array, namespace)
|
16
16
|
class_name.constantize
|
17
17
|
rescue NameError
|
18
18
|
raise NameError, "You should define #{class_name} decorator class."
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def decorator_class_name(
|
23
|
-
klass = if Tramway::Decorators::CollectionDecorators.collection?(
|
24
|
-
|
22
|
+
def decorator_class_name(object_or_array_or_class, namespace)
|
23
|
+
klass = if Tramway::Decorators::CollectionDecorators.collection?(object_or_array_or_class)
|
24
|
+
object_or_array_or_class.first.class
|
25
|
+
elsif object_or_array_or_class.is_a?(Class)
|
26
|
+
object_or_array_or_class
|
25
27
|
else
|
26
|
-
|
28
|
+
object_or_array_or_class.class
|
27
29
|
end
|
28
30
|
|
29
|
-
Tramway::Decorators::NameBuilder.default_decorator_class_name(klass)
|
31
|
+
base_class_name = Tramway::Decorators::NameBuilder.default_decorator_class_name(klass)
|
32
|
+
|
33
|
+
namespace.present? ? "#{namespace.to_s.camelize}::#{base_class_name}" : base_class_name
|
30
34
|
end
|
31
35
|
|
32
36
|
# :reek:NilCheck { enabled: false }
|
@@ -9,7 +9,23 @@ module Tramway
|
|
9
9
|
def property(attribute)
|
10
10
|
@properties << attribute
|
11
11
|
|
12
|
-
|
12
|
+
define_method(attribute) do
|
13
|
+
if object.respond_to?(attribute)
|
14
|
+
object.public_send(attribute)
|
15
|
+
else
|
16
|
+
raise NoMethodError, "#{self.class}##{attribute} is not defined"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
set_method = "#{attribute}="
|
21
|
+
|
22
|
+
define_method(set_method) do |value|
|
23
|
+
if object.respond_to?(set_method)
|
24
|
+
object.public_send(set_method, value)
|
25
|
+
else
|
26
|
+
raise NoMethodError, "#{self.class}##{set_method} is not defined"
|
27
|
+
end
|
28
|
+
end
|
13
29
|
end
|
14
30
|
|
15
31
|
def properties(*attributes)
|
@@ -8,12 +8,12 @@ module Tramway
|
|
8
8
|
#
|
9
9
|
module DecorateHelper
|
10
10
|
# :reek:NilCheck { enabled: false } because checking for nil is not a type-checking issue but business logic
|
11
|
-
def tramway_decorate(object_or_array, decorator: nil)
|
11
|
+
def tramway_decorate(object_or_array, decorator: nil, namespace: nil)
|
12
12
|
return [] if Tramway::Decorators::CollectionDecorators.collection?(object_or_array) && object_or_array.empty?
|
13
13
|
|
14
14
|
return if object_or_array.nil?
|
15
15
|
|
16
|
-
Tramway::Decorators::ClassHelper.decorator_class(object_or_array, decorator).decorate object_or_array
|
16
|
+
Tramway::Decorators::ClassHelper.decorator_class(object_or_array, decorator, namespace).decorate object_or_array
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -6,8 +6,8 @@ module Tramway
|
|
6
6
|
module Helpers
|
7
7
|
# Provides navbar helpers for ActionView
|
8
8
|
module NavbarHelper
|
9
|
-
def tramway_navbar(**options)
|
10
|
-
initialize_navbar
|
9
|
+
def tramway_navbar(with_entities: true, **options)
|
10
|
+
initialize_navbar(with_entities:)
|
11
11
|
|
12
12
|
yield @navbar if block_given?
|
13
13
|
|
@@ -18,8 +18,8 @@ module Tramway
|
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def initialize_navbar
|
22
|
-
@navbar = Tramway::Navbar.new self
|
21
|
+
def initialize_navbar(with_entities:)
|
22
|
+
@navbar = Tramway::Navbar.new self, with_entities:
|
23
23
|
end
|
24
24
|
|
25
25
|
def assign_navbar_items(options)
|
data/lib/tramway/navbar.rb
CHANGED
@@ -5,16 +5,18 @@ module Tramway
|
|
5
5
|
class Navbar
|
6
6
|
attr_reader :items, :context
|
7
7
|
|
8
|
-
def initialize(context)
|
8
|
+
def initialize(context, with_entities:)
|
9
9
|
@context = context
|
10
10
|
@items = { left: [], right: [] }
|
11
11
|
@filling = nil
|
12
12
|
|
13
|
-
|
13
|
+
if with_entities
|
14
|
+
entities = Tramway.config.entities
|
14
15
|
|
15
|
-
|
16
|
+
return unless entities.any?
|
16
17
|
|
17
|
-
|
18
|
+
preset_left entities
|
19
|
+
end
|
18
20
|
end
|
19
21
|
|
20
22
|
def left
|
data/lib/tramway/version.rb
CHANGED
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: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kalashnikovisme
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-06-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: anyway_config
|
@@ -219,6 +219,7 @@ files:
|
|
219
219
|
- lib/tramway/base_decorator.rb
|
220
220
|
- lib/tramway/base_form.rb
|
221
221
|
- lib/tramway/config.rb
|
222
|
+
- lib/tramway/configs/entities/page.rb
|
222
223
|
- lib/tramway/configs/entities/route.rb
|
223
224
|
- lib/tramway/configs/entity.rb
|
224
225
|
- lib/tramway/decorators/association.rb
|