tramway 0.2.3 → 0.3.1
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 +178 -4
- data/app/components/tailwinds/form/builder.rb +34 -0
- data/app/components/tailwinds/form/file_field_component.html.haml +4 -0
- data/app/components/tailwinds/form/file_field_component.rb +14 -0
- data/app/components/tailwinds/form/submit_button_component.html.haml +4 -0
- data/app/components/tailwinds/form/submit_button_component.rb +14 -0
- data/app/components/tailwinds/form/text_field_component.html.haml +4 -0
- data/app/components/tailwinds/form/text_field_component.rb +14 -0
- data/lib/tramway/base_form.rb +76 -0
- data/lib/tramway/engine.rb +12 -0
- data/lib/tramway/forms/class_helper.rb +22 -0
- data/lib/tramway/helpers/form_helper.rb +15 -0
- data/lib/tramway/helpers/navbar_helper.rb +1 -1
- data/lib/tramway/helpers/views_helper.rb +12 -0
- data/lib/tramway/version.rb +1 -1
- data/lib/tramway/views/form_builder.rb +16 -0
- data/lib/tramway.rb +2 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 817cf3ad464278dbd553e1e6d48c740a85305ca7ad8e4caac6a3d2fecc87041a
|
4
|
+
data.tar.gz: c3b5dadac4b6283f251c5647cb1d5dd2703c858a3e2a4730e0a80424b7c71e1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '079777145b30d2bf2695a14c71b77ef0470a5e29ddd4c0b2bcc43f14bf711e9f96d674bfa814b84eb7dda438167ddaa584717aa52039c2b1a2e8b787ac7e8719'
|
7
|
+
data.tar.gz: be3904286806a86296274d42026d11a00308e80eb5fa14e0f7e794fc78e70b70040a813f727c2723b7b87289b6ff355e068a30a3cbe2ea4bdd95ee9680ca0258
|
data/README.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
# Tramway
|
2
2
|
Unite Ruby on Rails brilliance. Streamline development with Tramway.
|
3
3
|
|
4
|
+
* [Installation](https://github.com/Purple-Magic/tramway#installation)
|
5
|
+
* [Usage](https://github.com/Purple-Magic/tramway#usage)
|
6
|
+
* [Tramway Entities](https://github.com/Purple-Magic/tramway#tramway-entities)
|
7
|
+
* [Tramway Decorators](https://github.com/Purple-Magic/tramway#tramway-decorators)
|
8
|
+
* [Tramway Form](https://github.com/Purple-Magic/tramway#tramway-form)
|
9
|
+
* [Tramway Navbar](https://github.com/Purple-Magic/tramway#tramway-navbar)
|
10
|
+
* [Tailwind-styled forms](https://github.com/Purple-Magic/tramway#tailwind-styled-forms)
|
11
|
+
|
4
12
|
## Installation
|
5
13
|
Add this line to your application's Gemfile:
|
6
14
|
|
@@ -43,10 +51,6 @@ Tramway.configure do |config|
|
|
43
51
|
end
|
44
52
|
```
|
45
53
|
|
46
|
-
### Tailwind components
|
47
|
-
|
48
|
-
Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are implemented with [ViewComponent](https://github.com/viewcomponent/view_component).
|
49
|
-
|
50
54
|
### Tramway Decorators
|
51
55
|
|
52
56
|
Tramway provides convenient decorators for your objects. **NOTE:** This is not the decorator pattern in its usual representation.
|
@@ -97,6 +101,152 @@ def show
|
|
97
101
|
end
|
98
102
|
```
|
99
103
|
|
104
|
+
### Tramway Form
|
105
|
+
|
106
|
+
Tramway provides **convenient** form objects for Rails applications. List properties you want to change and the rules in Form classes. No controllers overloading.
|
107
|
+
|
108
|
+
*app/forms/user_form.rb
|
109
|
+
```ruby
|
110
|
+
class UserForm < Tramway::BaseForm
|
111
|
+
properties :email, :password, :first_name, :last_name, :phone
|
112
|
+
|
113
|
+
def password=(value)
|
114
|
+
object.password = value if value.present?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
**Controllers without Tramway Form**
|
120
|
+
|
121
|
+
*app/controllers/users_controller.rb*
|
122
|
+
```ruby
|
123
|
+
class UsersController < ApplicationController
|
124
|
+
def create
|
125
|
+
@user = User.new
|
126
|
+
if @user.save user_params
|
127
|
+
render :show
|
128
|
+
else
|
129
|
+
render :new
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def update
|
134
|
+
@user = User.find params[:id]
|
135
|
+
if @user.save user_params
|
136
|
+
render :show
|
137
|
+
else
|
138
|
+
render :edit
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def user_params
|
145
|
+
params[:user].permit(:email, :password, :first_name, :last_name, :phone)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
**Controllers with Tramway Form**
|
151
|
+
|
152
|
+
*app/controllers/users_controller.rb*
|
153
|
+
```ruby
|
154
|
+
class UsersController < ApplicationController
|
155
|
+
def create
|
156
|
+
@user = tramway_form User.new
|
157
|
+
if @user.submit params[:user]
|
158
|
+
render :show
|
159
|
+
else
|
160
|
+
render :new
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def update
|
165
|
+
@user = tramway_form User.find params[:id]
|
166
|
+
if @user.submit params[:user]
|
167
|
+
render :show
|
168
|
+
else
|
169
|
+
render :edit
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
#### Implement Form objects for any case
|
176
|
+
|
177
|
+
*app/forms/user_updating_email_form.rb*
|
178
|
+
```ruby
|
179
|
+
class UserUpdatingEmailForm < Tramway::BaseForm
|
180
|
+
properties :email
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
*app/controllers/updating_emails_controller.rb*
|
185
|
+
```ruby
|
186
|
+
def update
|
187
|
+
@user = UserUpdatingEmailForm.new User.find params[:id]
|
188
|
+
if @user.submit params[:user]
|
189
|
+
# success
|
190
|
+
else
|
191
|
+
# failure
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
#### Create form namespaces
|
197
|
+
|
198
|
+
*app/forms/admin/user_form.rb*
|
199
|
+
```ruby
|
200
|
+
class Admin::UserForm < Tramway::BaseForm
|
201
|
+
properties :email, :password, :first_name, :last_name, :etc
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
*app/controllers/admin/users_controller.rb*
|
206
|
+
```ruby
|
207
|
+
class Admin::UsersController < Admin::ApplicationController
|
208
|
+
def create
|
209
|
+
@user = tramway_form User.new, namespace: :admin
|
210
|
+
if @user.submit params[:user]
|
211
|
+
render :show
|
212
|
+
else
|
213
|
+
render :new
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def update
|
218
|
+
@user = tramway_form User.find(params[:id]), namespace: :admin
|
219
|
+
if @user.submit params[:user]
|
220
|
+
render :show
|
221
|
+
else
|
222
|
+
render :edit
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
### Make flexible and extendable forms
|
229
|
+
|
230
|
+
Tramway Form properties are not mapped to a model. You're able to make extended forms.
|
231
|
+
|
232
|
+
*app/forms/user_form.rb*
|
233
|
+
```ruby
|
234
|
+
class UserForm < Tramway::BaseForm
|
235
|
+
properties :email, :password, :full_name
|
236
|
+
|
237
|
+
# RULE: in case password is empty, don't save
|
238
|
+
def password=(value)
|
239
|
+
object.password = value if value.present?
|
240
|
+
end
|
241
|
+
|
242
|
+
# EXTENDED FIELD: full name
|
243
|
+
def full_name=(value)
|
244
|
+
object.first_name = value.split(' ').first
|
245
|
+
object.last_name = value.split(' ').last
|
246
|
+
end
|
247
|
+
end
|
248
|
+
```
|
249
|
+
|
100
250
|
### Tramway Navbar
|
101
251
|
|
102
252
|
Tramway provides DSL for rendering Tailwind Navgiation bar.
|
@@ -163,6 +313,30 @@ tramway_navbar title: 'Purple Magic' do |nav|
|
|
163
313
|
end
|
164
314
|
```
|
165
315
|
|
316
|
+
### Tailwind-styled forms
|
317
|
+
|
318
|
+
Tramway uses [Tailwind](https://tailwindcss.com/) by default. All UI helpers are implemented with [ViewComponent](https://github.com/viewcomponent/view_component).
|
319
|
+
|
320
|
+
#### tramway_form_for
|
321
|
+
|
322
|
+
Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by default.
|
323
|
+
|
324
|
+
```ruby
|
325
|
+
= tramway_form_for User.new do |f|
|
326
|
+
= f.text_field :text
|
327
|
+
= f.password_field :password
|
328
|
+
= f.file_field :file
|
329
|
+
= f.submit "Create User"
|
330
|
+
```
|
331
|
+
|
332
|
+
will render [this](https://play.tailwindcss.com/xho3LfjKkK)
|
333
|
+
|
334
|
+
Available form helpers:
|
335
|
+
* text_field
|
336
|
+
* password_field
|
337
|
+
* file_field
|
338
|
+
* submit
|
339
|
+
|
166
340
|
## Contributing
|
167
341
|
|
168
342
|
Install [lefthook](https://github.com/evilmartians/lefthook)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tailwinds
|
4
|
+
module Form
|
5
|
+
# Provides Tailwind-styled forms
|
6
|
+
class Builder < Tramway::Views::FormBuilder
|
7
|
+
def text_field(attribute, **options, &)
|
8
|
+
input = super(attribute, **options.merge(class: text_input_class))
|
9
|
+
render(Tailwinds::Form::TextFieldComponent.new(input, attribute, object_name:, **options), &)
|
10
|
+
end
|
11
|
+
|
12
|
+
def password_field(attribute, **options, &)
|
13
|
+
input = super(attribute, **options.merge(class: text_input_class))
|
14
|
+
render(Tailwinds::Form::TextFieldComponent.new(input, attribute, object_name:, **options), &)
|
15
|
+
end
|
16
|
+
|
17
|
+
def file_field(attribute, **options, &)
|
18
|
+
input = super(attribute, **options.merge(class: :hidden))
|
19
|
+
|
20
|
+
render(Tailwinds::Form::FileFieldComponent.new(input, attribute, object_name:, **options), &)
|
21
|
+
end
|
22
|
+
|
23
|
+
def submit(action, **options, &)
|
24
|
+
render(Tailwinds::Form::SubmitButtonComponent.new(action, **options), &)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def text_input_class
|
30
|
+
'w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tailwinds
|
4
|
+
module Form
|
5
|
+
# Tailwind-styled file_field input
|
6
|
+
class FileFieldComponent < TailwindComponent
|
7
|
+
def initialize(input, attribute, object_name: nil, **options)
|
8
|
+
@label = options[:label] || attribute.to_s.capitalize
|
9
|
+
@for = "#{object_name}_#{attribute}"
|
10
|
+
@input = input
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tailwinds
|
4
|
+
module Form
|
5
|
+
# Tailwind-styled submit button
|
6
|
+
class SubmitButtonComponent < TailwindComponent
|
7
|
+
def initialize(action, **options)
|
8
|
+
@options = options.except :type
|
9
|
+
|
10
|
+
@text = action.is_a?(String) ? action : action.to_s.capitalize
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tailwinds
|
4
|
+
module Form
|
5
|
+
# Tailwind-styled text field
|
6
|
+
class TextFieldComponent < TailwindComponent
|
7
|
+
def initialize(input, attribute, object_name: nil, **options)
|
8
|
+
@label = options[:label] || attribute.to_s.humanize
|
9
|
+
@for = "#{object_name}_#{attribute}"
|
10
|
+
@input = input
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tramway
|
4
|
+
# Provides form object for Tramway
|
5
|
+
#
|
6
|
+
class BaseForm
|
7
|
+
attr_reader :object
|
8
|
+
|
9
|
+
%i[model_name to_key to_model errors attributes].each do |method_name|
|
10
|
+
delegate method_name, to: :object
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(object)
|
14
|
+
@object = object
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def property(attribute, _proc_obj = nil)
|
19
|
+
@properties ||= []
|
20
|
+
@properties << attribute
|
21
|
+
|
22
|
+
delegate attribute, to: :object
|
23
|
+
end
|
24
|
+
|
25
|
+
def properties(*attributes)
|
26
|
+
if attributes.any?
|
27
|
+
attributes.each do |attribute|
|
28
|
+
property(attribute)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@properties || []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def submit(params)
|
37
|
+
__submit params
|
38
|
+
|
39
|
+
object.save.tap do
|
40
|
+
__object
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def submit!(params)
|
45
|
+
__submit params
|
46
|
+
|
47
|
+
object.save!.tap do
|
48
|
+
__object
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(method_name, *args)
|
53
|
+
if method_name.to_s.end_with?('=') && args.count == 1
|
54
|
+
object.public_send(method_name, args.first)
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to_missing?(method_name, include_private = false)
|
61
|
+
method_name.to_s.end_with?('=') || super
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def __submit(params)
|
67
|
+
self.class.properties.each do |attribute|
|
68
|
+
public_send("#{attribute}=", params[attribute])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def __object
|
73
|
+
object.persisted? ? object.reload : object
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/tramway/engine.rb
CHANGED
@@ -13,11 +13,23 @@ module Tramway
|
|
13
13
|
loaded_class.include Tramway::Helpers::NavbarHelper
|
14
14
|
end
|
15
15
|
|
16
|
+
ActiveSupport.on_load(:action_view) do |loaded_class|
|
17
|
+
require 'tramway/helpers/views_helper'
|
18
|
+
|
19
|
+
loaded_class.include Tramway::Helpers::ViewsHelper
|
20
|
+
end
|
21
|
+
|
16
22
|
ActiveSupport.on_load(:action_controller) do |loaded_class|
|
17
23
|
require 'tramway/helpers/decorate_helper'
|
18
24
|
|
19
25
|
loaded_class.include Tramway::Helpers::DecorateHelper
|
20
26
|
end
|
27
|
+
|
28
|
+
ActiveSupport.on_load(:action_controller) do |loaded_class|
|
29
|
+
require 'tramway/helpers/form_helper'
|
30
|
+
|
31
|
+
loaded_class.include Tramway::Helpers::FormHelper
|
32
|
+
end
|
21
33
|
end
|
22
34
|
end
|
23
35
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tramway
|
4
|
+
module Forms
|
5
|
+
# Provides method to determine decorators classes
|
6
|
+
module ClassHelper
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def form_class(object, form, namespace)
|
10
|
+
object_class = object.class
|
11
|
+
|
12
|
+
if form.present?
|
13
|
+
form
|
14
|
+
elsif namespace.present?
|
15
|
+
"#{namespace.to_s.camelize}::#{object_class}Form".constantize
|
16
|
+
else
|
17
|
+
"#{object_class}Form".constantize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tramway/forms/class_helper'
|
4
|
+
|
5
|
+
module Tramway
|
6
|
+
module Helpers
|
7
|
+
# Provides methods into Rails ActionController
|
8
|
+
#
|
9
|
+
module FormHelper
|
10
|
+
def tramway_form(object, form: nil, namespace: nil)
|
11
|
+
Tramway::Forms::ClassHelper.form_class(object, form, namespace).new object
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tramway
|
4
|
+
module Helpers
|
5
|
+
# Provides view-oriented helpers for ActionView
|
6
|
+
module ViewsHelper
|
7
|
+
def tramway_form_for(object, *args, **options, &)
|
8
|
+
form_for(object, *args, **options.merge(builder: Tailwinds::Form::Builder), &)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/tramway/version.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tramway
|
4
|
+
module Views
|
5
|
+
# ActionView Form Builder helps us use ViewComponent as form helpers
|
6
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
7
|
+
attr_reader :template
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def render(component, &)
|
12
|
+
component.render_in(template, &)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/tramway.rb
CHANGED
@@ -4,7 +4,9 @@ require 'types'
|
|
4
4
|
require 'tramway/version'
|
5
5
|
require 'tramway/engine'
|
6
6
|
require 'tramway/base_decorator'
|
7
|
+
require 'tramway/base_form'
|
7
8
|
require 'tramway/config'
|
9
|
+
require 'tramway/views/form_builder'
|
8
10
|
require 'view_component/compiler'
|
9
11
|
require 'view_component/engine'
|
10
12
|
|
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.
|
4
|
+
version: 0.3.1
|
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: 2023-
|
12
|
+
date: 2023-10-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dry-struct
|
@@ -93,6 +93,13 @@ files:
|
|
93
93
|
- Rakefile
|
94
94
|
- app/components/tailwind_component.html.haml
|
95
95
|
- app/components/tailwind_component.rb
|
96
|
+
- app/components/tailwinds/form/builder.rb
|
97
|
+
- app/components/tailwinds/form/file_field_component.html.haml
|
98
|
+
- app/components/tailwinds/form/file_field_component.rb
|
99
|
+
- app/components/tailwinds/form/submit_button_component.html.haml
|
100
|
+
- app/components/tailwinds/form/submit_button_component.rb
|
101
|
+
- app/components/tailwinds/form/text_field_component.html.haml
|
102
|
+
- app/components/tailwinds/form/text_field_component.rb
|
96
103
|
- app/components/tailwinds/nav/item_component.html.haml
|
97
104
|
- app/components/tailwinds/nav/item_component.rb
|
98
105
|
- app/components/tailwinds/navbar_component.html.haml
|
@@ -102,17 +109,22 @@ files:
|
|
102
109
|
- lib/tasks/tramway_tasks.rake
|
103
110
|
- lib/tramway.rb
|
104
111
|
- lib/tramway/base_decorator.rb
|
112
|
+
- lib/tramway/base_form.rb
|
105
113
|
- lib/tramway/config.rb
|
106
114
|
- lib/tramway/configs/entities/route.rb
|
107
115
|
- lib/tramway/configs/entity.rb
|
108
116
|
- lib/tramway/decorators/class_helper.rb
|
109
117
|
- lib/tramway/decorators/collection_decorator.rb
|
110
118
|
- lib/tramway/engine.rb
|
119
|
+
- lib/tramway/forms/class_helper.rb
|
111
120
|
- lib/tramway/helpers/decorate_helper.rb
|
121
|
+
- lib/tramway/helpers/form_helper.rb
|
112
122
|
- lib/tramway/helpers/navbar_helper.rb
|
123
|
+
- lib/tramway/helpers/views_helper.rb
|
113
124
|
- lib/tramway/navbar.rb
|
114
125
|
- lib/tramway/utils/render.rb
|
115
126
|
- lib/tramway/version.rb
|
127
|
+
- lib/tramway/views/form_builder.rb
|
116
128
|
- lib/types.rb
|
117
129
|
homepage: https://github.com/purple-magic/tramway
|
118
130
|
licenses:
|