tramway 0.3.1.2 → 0.4

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: 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