tramway 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eee7e8b57c93fd646e96a26e07e9dab81b6deee977c50c2ab34b1df93f48e96d
4
- data.tar.gz: 48d411dcce7d057eb867862f2b8410b5320bbf2dfdc47f5a0e06508f34049139
3
+ metadata.gz: 817cf3ad464278dbd553e1e6d48c740a85305ca7ad8e4caac6a3d2fecc87041a
4
+ data.tar.gz: c3b5dadac4b6283f251c5647cb1d5dd2703c858a3e2a4730e0a80424b7c71e1a
5
5
  SHA512:
6
- metadata.gz: dc78481a707c0688c774469549b8c1dc319adfe51f67cdd871b4b4751f03367206c674298c764a54110aa9c9b634082c6c844e1b97df0e9d2b2411fb536fa527
7
- data.tar.gz: 2bdf9eca337acd11afacb0fca442e6047b1b600d5b6a499bad31ad7522777edf0c2079c247491334a32eb3bb8682d54c78e596256b120c14a6c4a62593592150
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,4 @@
1
+ .mb-4
2
+ %label.inline-block.bg-blue-500.hover:bg-blue-700.text-white.font-bold.py-2.px-4.rounded.cursor-pointer.mt-4{ for: @for }
3
+ = @label
4
+ = @input
@@ -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,4 @@
1
+ .flex.items-center.justify-between
2
+ %button.bg-red-500.hover:bg-red-700.text-white.font-bold.py-2.px-4.rounded.focus:outline-none.focus:shadow-outline{ type: :submit, name: :commit, **@options }
3
+ = @text
4
+ = @content
@@ -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,4 @@
1
+ .mb-4
2
+ %label.block.text-gray-700.text-sm.font-bold.mb-2{ for: @for }
3
+ = @label
4
+ = @input
@@ -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
@@ -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
@@ -4,7 +4,7 @@ require 'tramway/navbar'
4
4
 
5
5
  module Tramway
6
6
  module Helpers
7
- # Providing navbar helpers for ActionView
7
+ # Provides navbar helpers for ActionView
8
8
  module NavbarHelper
9
9
  def tramway_navbar(**options)
10
10
  initialize_navbar
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '0.2.3'
4
+ VERSION = '0.3.1'
5
5
  end
@@ -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.2.3
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-08-17 00:00:00.000000000 Z
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: