tramway 0.3.1.2 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0becd1c6b67984097bf36b6e6a2180792863ded76f5e28174ea213e3f5f85ddd
4
- data.tar.gz: a74d5284069e8fa7c162b5c1dce27ba3fc85bb3558c0c934527b2365ab37bf8d
3
+ metadata.gz: 68a5dd780d7427b0c2075f140c891af3a4c33210c88a00d86e33279dc27e6c28
4
+ data.tar.gz: 63701d608f5545ea95e98fac522624cc34092199fe839b16ba579d9c461c0383
5
5
  SHA512:
6
- metadata.gz: 45a89fec8a66c5e33f3321d18dfb7971bb0fe52f2c1984b86958453364af2c7327d8fba8a80a36f83e3cb40aa93da5aae2cd2b319658dbf1ca21cea122375692
7
- data.tar.gz: e3087975aa2d174982434340b32964c39817c7a95baabbb0f912c54dbfda682e3c1270d6600896ac5ff9ad94c0bebbba59de4ea724933b96c2a1cb95efcb0861
6
+ metadata.gz: 8e6c295607acc32db6b7a0bea211ddb9ba2d8e2ca141804bce78b7b51fb74a08478c38ead05962f56ecd226a57b2e7398b97c86ac6dcc7aa46c3862bd8a49f30
7
+ data.tar.gz: bd89050c6d5b3391f457eea7a277cdaaa53c3fbe1f20a0567c39b659ab8a3187d2f161bed38a6b9226782191b884fe94eb63d7e7f8aa292e8ff121dcf149c521
data/README.md CHANGED
@@ -8,6 +8,7 @@ Unite Ruby on Rails brilliance. Streamline development with Tramway.
8
8
  * [Tramway Form](https://github.com/Purple-Magic/tramway#tramway-form)
9
9
  * [Tramway Navbar](https://github.com/Purple-Magic/tramway#tramway-navbar)
10
10
  * [Tailwind-styled forms](https://github.com/Purple-Magic/tramway#tailwind-styled-forms)
11
+ * [Tailwind-styled pagination](https://github.com/Purple-Magic/tramway#tailwind-styled-pagination)
11
12
 
12
13
  ## Installation
13
14
  Add this line to your application's Gemfile:
@@ -81,7 +82,7 @@ class UserDecorator < Tramway::BaseDecorator
81
82
  end
82
83
  ```
83
84
 
84
- #### Decorate single object
85
+ #### Decorate a single object
85
86
 
86
87
  You can use the same method to decorate a single object either
87
88
 
@@ -91,6 +92,20 @@ def show
91
92
  end
92
93
  ```
93
94
 
95
+ #### Decorate a collection of objects
96
+
97
+ ```ruby
98
+ def index
99
+ @users = tramway_decorate User.all
100
+ end
101
+ ```
102
+
103
+ ```ruby
104
+ def index
105
+ @posts = tramway_decorate user.posts
106
+ end
107
+ ```
108
+
94
109
  #### Decorate with a specific decorator
95
110
 
96
111
  You can implement a specific decorator and ask Tramway to decorate with it
@@ -334,6 +349,7 @@ Tramway provides `tramway_form_for` helper that renders Tailwind-styled forms by
334
349
  = tramway_form_for User.new do |f|
335
350
  = f.text_field :text
336
351
  = f.password_field :password
352
+ = f.select :role, [:admin, :user]
337
353
  = f.file_field :file
338
354
  = f.submit "Create User"
339
355
  ```
@@ -344,8 +360,35 @@ Available form helpers:
344
360
  * text_field
345
361
  * password_field
346
362
  * file_field
363
+ * select
347
364
  * submit
348
365
 
366
+ ### Tailwind-styled pagination for Kaminari
367
+
368
+ Tramway uses [Tailwind](https://tailwindcss.com/) by default. It has tailwind-styled pagination for [kaminari](https://github.com/kaminari/kaminari).
369
+
370
+ #### How to use
371
+
372
+ *Gemfile*
373
+ ```ruby
374
+ gem 'tramway'
375
+ gem 'kaminari'
376
+ ```
377
+
378
+ *config/initializers/tramway.rb*
379
+ ```ruby
380
+ Tramway.configure do |config|
381
+ config.pagination = { enabled: true } # enabled is false by default
382
+ end
383
+ ```
384
+
385
+ *app/views/users/index.html.haml*
386
+ ```haml
387
+ = paginate @users # it will render tailwind-styled pagination buttons by default
388
+ ```
389
+
390
+ Pagination buttons looks like [this](https://play.tailwindcss.com/mqgDS5l9oY)
391
+
349
392
  ## Contributing
350
393
 
351
394
  Install [lefthook](https://github.com/evilmartians/lefthook)
@@ -353,6 +396,7 @@ Install [lefthook](https://github.com/evilmartians/lefthook)
353
396
  ```
354
397
  bundle
355
398
  lefthook install
399
+ rspec
356
400
  ```
357
401
 
358
402
  ## License
@@ -4,4 +4,12 @@ require 'view_component'
4
4
 
5
5
  # Base TailwindComponent. Contains base features for all tailwind components
6
6
  class TailwindComponent < ViewComponent::Base
7
+ extend Dry::Initializer[undefined: false]
8
+
9
+ option :input
10
+ option :attribute
11
+ option :value, optional: true
12
+ option :options
13
+ option :label
14
+ option :for
7
15
  end
@@ -3,21 +3,36 @@
3
3
  module Tailwinds
4
4
  module Form
5
5
  # Provides Tailwind-styled forms
6
+ # :reek:InstanceVariableAssumption
6
7
  class Builder < Tramway::Views::FormBuilder
7
8
  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), &)
9
+ render(Tailwinds::Form::TextFieldComponent.new(
10
+ input: input(:text_field),
11
+ value: get_value(attribute, options),
12
+ **default_options(attribute, options)
13
+ ), &)
10
14
  end
11
15
 
12
16
  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), &)
17
+ render(Tailwinds::Form::TextFieldComponent.new(
18
+ input: input(:password_field),
19
+ **default_options(attribute, options)
20
+ ), &)
15
21
  end
16
22
 
17
23
  def file_field(attribute, **options, &)
18
24
  input = super(attribute, **options.merge(class: :hidden))
19
25
 
20
- render(Tailwinds::Form::FileFieldComponent.new(input, attribute, object_name:, **options), &)
26
+ render(Tailwinds::Form::FileFieldComponent.new(input:, **default_options(attribute, options)), &)
27
+ end
28
+
29
+ def select(attribute, collection, **options, &)
30
+ render(Tailwinds::Form::SelectComponent.new(
31
+ input: input(:select),
32
+ value: options[:selected] || object.public_send(attribute),
33
+ collection:,
34
+ **default_options(attribute, options)
35
+ ), &)
21
36
  end
22
37
 
23
38
  def submit(action, **options, &)
@@ -26,8 +41,31 @@ module Tailwinds
26
41
 
27
42
  private
28
43
 
29
- def text_input_class
30
- 'w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500'
44
+ def input(method_name)
45
+ unbound_method = self.class.superclass.instance_method(method_name)
46
+ unbound_method.bind(self)
47
+ end
48
+
49
+ def get_value(attribute, options)
50
+ options[:value] || object.public_send(attribute)
51
+ end
52
+
53
+ def default_options(attribute, options)
54
+ {
55
+ attribute:,
56
+ label: label(attribute, options),
57
+ for: for_id(attribute),
58
+ options:
59
+ }
60
+ end
61
+
62
+ # :reek:UtilityFunction
63
+ def label(attribute, options)
64
+ options[:label] || attribute.to_s.humanize
65
+ end
66
+
67
+ def for_id(attribute)
68
+ "#{object_name}_#{attribute}"
31
69
  end
32
70
  end
33
71
  end
@@ -4,11 +4,7 @@ module Tailwinds
4
4
  module Form
5
5
  # Tailwind-styled file_field input
6
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
7
+ option :input
12
8
  end
13
9
  end
14
10
  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.call(@attribute, @collection, { selected: @value }, @options.merge(class: 'bg-white border border-gray-300 text-gray-700 py-2 px-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent'))
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tailwinds
4
+ module Form
5
+ # Tailwind-styled text field
6
+ class SelectComponent < TailwindComponent
7
+ option :collection
8
+ end
9
+ end
10
+ end
@@ -1,4 +1,4 @@
1
1
  .mb-4
2
2
  %label.block.text-gray-700.text-sm.font-bold.mb-2{ for: @for }
3
3
  = @label
4
- = @input
4
+ = @input.call @attribute, **@options.merge(class: 'w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:border-red-500'), value: @value
@@ -4,11 +4,6 @@ module Tailwinds
4
4
  module Form
5
5
  # Tailwind-styled text field
6
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
7
  end
13
8
  end
14
9
  end
@@ -0,0 +1,9 @@
1
+ -# Link to the "First" page
2
+ -# available local variables
3
+ -# url: url to the first page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.first{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote
@@ -0,0 +1,8 @@
1
+ -# Non-link tag that stands for skipped pages...
2
+ -# available local variables
3
+ -# current_page: a page object for the currently displayed page
4
+ -# total_pages: total number of pages
5
+ -# per_page: number of items to fetch per page
6
+ -# remote: data-remote
7
+ %span.page.gap{ class: 'px-3 py-2 text-sm font-medium text-purple-700' }
8
+ = t('views.pagination.truncate').html_safe
@@ -0,0 +1,9 @@
1
+ -# Link to the "Last" page
2
+ -# available local variables
3
+ -# url: url to the last page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.last{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote
@@ -0,0 +1,9 @@
1
+ -# Link to the "Next" page
2
+ -# available local variables
3
+ -# url: url to the next page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.next{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote
@@ -0,0 +1,14 @@
1
+ -# Link showing page number
2
+ -# available local variables
3
+ -# page: a page object for "this" page
4
+ -# url: url to this page
5
+ -# current_page: a page object for the currently displayed page
6
+ -# total_pages: total number of pages
7
+ -# per_page: number of items to fetch per page
8
+ -# remote: data-remote
9
+ - if page.current?
10
+ %span{class: "px-3 py-2 font-medium rounded-md bg-purple-500 text-white" }
11
+ = page
12
+ - else
13
+ %span{class: "cursor px-3 py-2 font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100"}
14
+ = link_to_unless page.current?, page, url, {remote: remote, rel: page.rel}
@@ -0,0 +1,12 @@
1
+ = paginator.render do
2
+ %nav.pagination.flex.items-center.justify-center.space-x-1
3
+ = first_page_tag unless current_page.first?
4
+ = prev_page_tag unless current_page.first?
5
+ - each_page do |page|
6
+ - if page.display_tag?
7
+ = page_tag page
8
+ - elsif !page.was_truncated?
9
+ = gap_tag
10
+ = next_page_tag unless current_page.last?
11
+ = last_page_tag unless current_page.last?
12
+
@@ -0,0 +1,9 @@
1
+ -# Link to the "Previous" page
2
+ -# available local variables
3
+ -# url: url to the previous page
4
+ -# current_page: a page object for the currently displayed page
5
+ -# total_pages: total number of pages
6
+ -# per_page: number of items to fetch per page
7
+ -# remote: data-remote
8
+ %span.prev{ class: 'px-3 py-2 text-sm font-medium text-purple-700 bg-white rounded-md hover:bg-purple-100' }
9
+ = link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote
@@ -12,6 +12,8 @@ module Tramway
12
12
 
13
13
  def initialize(object)
14
14
  @object = object
15
+
16
+ self.class.delegate object.class.primary_key, to: :object
15
17
  end
16
18
 
17
19
  class << self
@@ -1,26 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'anyway'
3
4
  require 'singleton'
4
5
  require 'tramway/configs/entity'
5
6
 
6
7
  module Tramway
7
8
  # Basic configuration of Tramway
8
9
  #
9
- class Config
10
+ class Config < Anyway::Config
10
11
  include Singleton
11
12
 
12
- def initialize
13
- @entities = []
14
- end
13
+ attr_config(
14
+ pagination: { enabled: false },
15
+ entities: []
16
+ )
15
17
 
16
18
  def entities=(collection)
17
- @entities = collection.map do |entity|
19
+ super(collection.map do |entity|
18
20
  entity_options = entity.is_a?(Hash) ? entity : { name: entity }
19
21
 
20
22
  Tramway::Configs::Entity.new(**entity_options)
21
- end
23
+ end)
22
24
  end
23
-
24
- attr_reader :entities
25
25
  end
26
26
  end
@@ -14,7 +14,7 @@ module Tramway
14
14
  end
15
15
 
16
16
  def collection?(object)
17
- object.class.name.in? ['ActiveRecord::Relation', 'Array']
17
+ object.is_a?(Array) || object.is_a?(ActiveRecord::Relation)
18
18
  end
19
19
  end
20
20
  end
@@ -7,29 +7,65 @@ module Tramway
7
7
  isolate_namespace Tramway
8
8
 
9
9
  initializer 'tramway.load_helpers' do
10
+ load_navbar_helper
11
+ load_views_helper
12
+ load_decorator_helper
13
+ load_form_helper
14
+ configure_pagination if Tramway.config.pagination[:enabled]
15
+ end
16
+
17
+ private
18
+
19
+ def load_navbar_helper
10
20
  ActiveSupport.on_load(:action_view) do |loaded_class|
11
21
  require 'tramway/helpers/navbar_helper'
12
22
 
13
23
  loaded_class.include Tramway::Helpers::NavbarHelper
14
24
  end
25
+ end
15
26
 
27
+ def load_views_helper
16
28
  ActiveSupport.on_load(:action_view) do |loaded_class|
17
29
  require 'tramway/helpers/views_helper'
18
30
 
19
31
  loaded_class.include Tramway::Helpers::ViewsHelper
20
32
  end
33
+ end
21
34
 
35
+ def load_decorator_helper
22
36
  ActiveSupport.on_load(:action_controller) do |loaded_class|
23
37
  require 'tramway/helpers/decorate_helper'
24
38
 
25
39
  loaded_class.include Tramway::Helpers::DecorateHelper
26
40
  end
41
+ end
27
42
 
43
+ def load_form_helper
28
44
  ActiveSupport.on_load(:action_controller) do |loaded_class|
29
45
  require 'tramway/helpers/form_helper'
30
46
 
31
47
  loaded_class.include Tramway::Helpers::FormHelper
32
48
  end
33
49
  end
50
+
51
+ # :reek:NestedIterators { enabled: false }
52
+ # :reek:TooManyStatements { enabled: false }
53
+ def configure_pagination
54
+ ActiveSupport.on_load(:action_controller) do
55
+ # Detecting tramway views path
56
+ tramway_spec = Gem.loaded_specs['tramway']
57
+ tramway_views_path = File.join(tramway_spec.full_gem_path, 'app/views')
58
+
59
+ paths = view_paths.to_ary
60
+
61
+ # Determine index to insert tramway views path
62
+ rails_views_index = paths.find_index { |path| path.to_s.ends_with?('app/views') }
63
+ insert_index = rails_views_index ? rails_views_index + 1 : 0
64
+
65
+ # Inserting tramway views path
66
+ paths.insert(insert_index, tramway_views_path)
67
+ self.view_paths = paths
68
+ end
69
+ end
34
70
  end
35
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tramway
4
- VERSION = '0.3.1.2'
4
+ VERSION = '0.4'
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: 0.3.1.2
4
+ version: '0.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: 2023-12-23 00:00:00.000000000 Z
12
+ date: 2024-01-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dry-struct
@@ -96,6 +96,8 @@ files:
96
96
  - app/components/tailwinds/form/builder.rb
97
97
  - app/components/tailwinds/form/file_field_component.html.haml
98
98
  - app/components/tailwinds/form/file_field_component.rb
99
+ - app/components/tailwinds/form/select_component.html.haml
100
+ - app/components/tailwinds/form/select_component.rb
99
101
  - app/components/tailwinds/form/submit_button_component.html.haml
100
102
  - app/components/tailwinds/form/submit_button_component.rb
101
103
  - app/components/tailwinds/form/text_field_component.html.haml
@@ -104,6 +106,13 @@ files:
104
106
  - app/components/tailwinds/nav/item_component.rb
105
107
  - app/components/tailwinds/navbar_component.html.haml
106
108
  - app/components/tailwinds/navbar_component.rb
109
+ - app/views/kaminari/_first_page.html.haml
110
+ - app/views/kaminari/_gap.html.haml
111
+ - app/views/kaminari/_last_page.html.haml
112
+ - app/views/kaminari/_next_page.html.haml
113
+ - app/views/kaminari/_page.html.haml
114
+ - app/views/kaminari/_paginator.html.haml
115
+ - app/views/kaminari/_prev_page.html.haml
107
116
  - config/routes.rb
108
117
  - lib/rules/turbo_html_attributes_rules.rb
109
118
  - lib/tasks/tramway_tasks.rake