lintity 0.1.0 → 0.2.0

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: dfa928e67ae36f6fabd78f3ca81dfdb437d2b3c0a113cb5e04d8d08a19101dc4
4
- data.tar.gz: 50cfef8c00bafcd82944e70b346b72b90ef0eb52587d59149f7e3a005df63da5
3
+ metadata.gz: 3f3c8d648489297bdd0cec0b7c656a9819067f94748661826b1890bc77960e61
4
+ data.tar.gz: a79f0c218e49d7b0c4671543083a43404066fdbfc05fb20fc1405bcf89f8e8d3
5
5
  SHA512:
6
- metadata.gz: c52bec211f5442cca8e4422487dfd544de53ed105e28c2ff21179bdf96c777efe103cf4a260ab18accaa871b3fc2b79dd6de4bf5acd1ebe6ba6c79c2ac5ff97f
7
- data.tar.gz: 68fbebac842f5042ffd78e67024dc61cfe92ec144cc614f58484257db1e50433b6e64d18a6dd5044d616a16f80c194b0d8ca632ec3228009b6d657b2c865aa2d
6
+ metadata.gz: 16ffd53277b5e0f91cec29991486ae54ffa74cf95c28da7ccd1fbe0c9c2e9c2fff2d8ea73145597987fe22cb428d19be3310df5c09b9056ac01ac6c5bcb9d830
7
+ data.tar.gz: 709bf74aa2cc41b61f7a65a4d38ff4044a5e846a9f42c7b192319bf5b1e4e079b570b549182d688abb8fc1c88fde46c6249fb5616f16533df685dd3ac59a5bd6
data/README.md CHANGED
@@ -18,12 +18,11 @@ Or install it yourself as:
18
18
  $ gem install lintity
19
19
  ```
20
20
  ## System Requirements
21
- * Ruby >= 3.0
22
- * Ruby On Rails >= 7.0
21
+ * Ruby >= 3.4
22
+ * Ruby On Rails >= 8.0
23
23
  * gem 'importmap-rails'
24
24
  * gem 'stimulus-rails'
25
25
  * bootstrap 5
26
- * gem 'font-awesome-rails'
27
26
  * ActiveRecord
28
27
 
29
28
  ## Usage
@@ -42,6 +41,8 @@ class CustomersController < Lintity::EntityListController #inherit
42
41
  else
43
42
  Customer.all
44
43
  end
44
+ # Pagination is automatically handled by the controller
45
+ # @records will be paginated using pagy if it responds to paginate
45
46
  end
46
47
 
47
48
  def edit; end
@@ -1,7 +1,10 @@
1
1
  module Lintity
2
2
  class EntityListController < ApplicationController
3
+ include Pagy::Method
3
4
  before_action :init_fields, only: [:index]
4
5
  before_action :check_filters, only: [:index]
6
+ before_action :init_records, only: [:index]
7
+ before_action :set_pagination, only: [:index]
5
8
 
6
9
  def index; end
7
10
 
@@ -34,5 +37,12 @@ module Lintity
34
37
  end
35
38
  @filter_field
36
39
  end
40
+
41
+ def set_pagination
42
+ @pagy, @records = pagy(:offset, @records) if @records
43
+ end
44
+
45
+ def init_records
46
+ end
37
47
  end
38
48
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lintity
4
+ class EntityReportController < ApplicationController
5
+ before_action :init_fields, only: [:index]
6
+ # Child controllers inheriting from this controller should set @records
7
+ # to an array of hashes with the structure described in the view.
8
+ def index
9
+ @records ||= []
10
+ end
11
+ end
12
+ end
@@ -1,4 +1,34 @@
1
+ require 'ostruct'
2
+
1
3
  module Lintity
2
4
  module ApplicationHelper
5
+ def numeric_filter_tag(filter_field, field_info)
6
+ button_tag "#{field_info[:name]}<i class=\"bi bi-funnel-fill #{'text-success' if filter_field == field_info[:field]}\"></i>".html_safe,
7
+ class: 'btn btn-link', data: { 'entity-list-field-param' => field_info[:field], 'action' => 'click->entity-list#numberic_filter' }
8
+ end
9
+
10
+ def filters_select_options
11
+ options_from_collection_for_select(
12
+ [OpenStruct.new(id: 'eq', name: '='), OpenStruct.new(id: 'less', name: '<='),
13
+ OpenStruct.new(id: 'more', name: '>=')], 'id', 'name'
14
+ )
15
+ end
16
+
17
+ # Renders a header with a caption and an optional "New" button.
18
+ # caption - String displayed as the header title.
19
+ # button_path - URL for the button; if nil or blank, the button is omitted.
20
+ def render_entity_list_header(caption, button_path)
21
+ content_tag(:div, class: 'entity-list-header d-flex justify-content-between align-items-center mb-3') do
22
+ concat content_tag(:h2, caption, class: 'mb-0')
23
+ concat link_to('New', button_path, class: 'btn btn-primary') if button_path.present?
24
+ end
25
+ end
26
+
27
+ def render_entity_report_header(caption, button_path)
28
+ content_tag(:div, class: 'entity-list-header d-flex justify-content-between align-items-center mb-3') do
29
+ concat content_tag(:h2, caption, class: 'mb-0')
30
+ concat link_to('PDF', button_path, class: 'btn btn-primary') if button_path.present?
31
+ end
32
+ end
3
33
  end
4
- end
34
+ end
@@ -1,3 +1,6 @@
1
+ <%# Header added by lintity gem: caption and new record button provided by host app %>
2
+ <%= render_entity_list_header(@entity_list_header_caption, @entity_list_new_path) %>
3
+
1
4
  <div data-controller="entity-list">
2
5
  <table class="table table-striped table-bordered">
3
6
  <thead>
@@ -11,7 +14,7 @@
11
14
  when 'info'
12
15
  field_info[:name]
13
16
  when 'numeric_filter'
14
- button_tag "#{field_info[:name]}<i class=\"fa fa-filter #{ 'text-success' if @filter_field == field_info[:field] }\"></i>".html_safe, class: 'btn btn-link', data: {'entity-list-field-param'=>field_info[:field], 'action'=>"click->entity-list#numberic_filter"}
17
+ numeric_filter_tag(@filter_field, field_info)
15
18
  end
16
19
  %>
17
20
  </th>
@@ -26,41 +29,44 @@
26
29
  <%=
27
30
  case field_info[:type]
28
31
  when 'edit'
29
- caption = record[field_info[:field]]
32
+ caption = record.public_send field_info[:field]
30
33
  link_to( caption ? caption : '(empty)', field_info[:path].call(record.id) )
31
34
  when 'info', 'numeric_filter'
32
- record[field_info[:field]]
35
+ record.public_send field_info[:field]
33
36
  end
34
37
  %>
35
38
  </td>
36
39
  <% end %>
37
40
  </tr>
38
- <% end %>
39
- </tbody>
40
- </table>
41
+ <% end %>
42
+ </tbody>
43
+ </table>
44
+
45
+ <% if @pagy %>
46
+ <%== @pagy.series_nav(:bootstrap) %>
47
+ <% end %>
41
48
 
42
- <!-- Modal -->
43
- <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" data-entity-list-target="modal">
44
- <div class="modal-dialog">
45
- <div class="modal-content">
46
- <div class="modal-header">
47
- <h5>Set Filter for "<span data-entity-list-target="fieldcaption"></span>"</h5>
48
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
49
- </div>
50
- <%= form_tag @search_path, method: :get, :enforce_utf8=>false do %>
51
- <div class="modal-body">
52
- <%= number_field_tag "field_replace", '', :class=>"form-control", :placeholder=>"Set filter value...", :autofocus=>'true', step: :any, 'data-entity-list-target'=>"fieldvalue" %>
53
- <br/>
54
- <%= select_tag :filter_option, options_from_collection_for_select([OpenStruct.new(id: 'eq', name: '='), OpenStruct.new(id: 'less', name: '<='), OpenStruct.new(id: 'more', name: '>=')], "id", "name"), :class=>"form-control" %>
55
-
56
- </div>
57
- <div class="modal-footer">
58
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
59
- <input type="submit" class="btn btn-primary" name="Save changes"/>
60
- </div>
61
- <% end %>
62
- </div>
63
- </div>
64
- </div>
49
+ <!-- Modal -->
50
+ <div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" data-entity-list-target="modal">
51
+ <div class="modal-dialog">
52
+ <div class="modal-content">
53
+ <div class="modal-header">
54
+ <h5>Set Filter for "<span data-entity-list-target="fieldcaption"></span>"</h5>
55
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
56
+ </div>
57
+ <%= form_tag @search_path, method: :get, :enforce_utf8=>false do %>
58
+ <div class="modal-body">
59
+ <%= number_field_tag "field_replace", '', :class=>"form-control", :placeholder=>"Set filter value...", :autofocus=>'true', step: :any, 'data-entity-list-target'=>"fieldvalue" %>
60
+ <br/>
61
+ <%= select_tag :filter_option, filters_select_options, :class=>"form-control" %>
62
+ </div>
63
+ <div class="modal-footer">
64
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
65
+ <input type="submit" class="btn btn-primary" name="Save changes"/>
66
+ </div>
67
+ <% end %>
68
+ </div>
69
+ </div>
70
+ </div>
65
71
 
66
- </div>
72
+ </div>
@@ -0,0 +1,23 @@
1
+ <%# Recursive partial to render a record and its children %>
2
+ <%# Expected local variable: record (hash with :details and :children) %>
3
+ <%# Determine nesting level; default to 1 for top‑level records %>
4
+ <% level = local_assigns[:level] || 1 %>
5
+ <% if record[:children].empty? %>
6
+ <tr>
7
+ <% else %>
8
+ <tr class="fs-<%= level + 4 %> fw-bold">
9
+ <% end %>
10
+ <% record[:details].each do |key, value| %>
11
+ <% field_setting = @fields_settings.find { |f| f[:field] == key.to_s } %>
12
+ <% if field_setting && field_setting[:type] == "number" %>
13
+ <td class="text-end"><%= value %></td>
14
+ <% else %>
15
+ <td><%= value %></td>
16
+ <% end %>
17
+ <% end %>
18
+ </tr>
19
+ <% if record[:children].present? %>
20
+ <% record[:children].each do |child| %>
21
+ <%= render partial: 'lintity/entity_report/record', locals: { record: child, level: level + 1 } %>
22
+ <% end %>
23
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%# PDF-specific partial for a single record. Reuse the HTML partial rendering logic %>
2
+ <%= render partial: 'lintity/entity_report/record', locals: { record: record }, formats: [:html] %>
3
+
@@ -0,0 +1,54 @@
1
+ <%# New nested @records format: each record has :details and optional :children %>
2
+ <%= render_entity_report_header(@entity_report_header_caption, @entity_report_pdf_path) %>
3
+ <%# Add optional filter description below the header if provided %>
4
+ <% if defined?(@entity_filter_header_caption) && @entity_filter_header_caption.present? %>
5
+ <div class="entity-filter-header-caption" style="margin-top: 0.5rem; font-weight: normal;">
6
+ <%= @entity_filter_header_caption %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <% if @records.any? %>
11
+ <table class="table table-bordered table-striped table-hover text-muted" style="width: 100%;">
12
+ <thead class="table-light">
13
+ <tr>
14
+ <% @records.first[:details].each_key do |key| %>
15
+ <% field_setting = @fields_settings.find { |f| f[:field] == key.to_s } %>
16
+ <% if field_setting && field_setting[:caption].present? %>
17
+ <th><%= field_setting[:caption] %></th>
18
+ <% else %>
19
+ <th><%= key.to_s.titleize %></th>
20
+ <% end %>
21
+ <% end %>
22
+ </tr>
23
+ </thead>
24
+ <tbody>
25
+ <% @records.each do |record| %>
26
+ <%= render partial: 'lintity/entity_report/record', locals: { record: record } %>
27
+ <% end %>
28
+ </tbody>
29
+ <%# Calculate sums for numeric fields across all records and children %>
30
+ <% sums = Hash.new(0) %>
31
+ <% @records.each do |rec| %>
32
+ <% rec[:details].each do |k, v| %>
33
+ <% field_setting = @fields_settings.find { |f| f[:field] == k.to_s } %>
34
+ <% if field_setting && field_setting[:type] == "number" %>
35
+ <% sums[k] += v if v.is_a?(Numeric) %>
36
+ <% end %>
37
+ <% end %>
38
+ <% end %>
39
+ <tfoot>
40
+ <tr class="fw-bold">
41
+ <% @records.first[:details].each_key do |key| %>
42
+ <% field_setting = @fields_settings.find { |f| f[:field] == key.to_s } %>
43
+ <% if field_setting && field_setting[:type] == "number" %>
44
+ <td class="text-end"><%= sums[key] %></td>
45
+ <% else %>
46
+ <td></td>
47
+ <% end %>
48
+ <% end %>
49
+ </tr>
50
+ </tfoot>
51
+ </table>
52
+ <% else %>
53
+ <div class="alert alert-info">No records found for the selected filters.</div>
54
+ <% end %>
@@ -0,0 +1,96 @@
1
+ <%# PDF version of the entity report, reusing the same markup as HTML %>
2
+ <%# The layout will be rendered with PDF-specific styles if provided %>
3
+ <%# This template mirrors app/views/lintity/entity_report/index.html.erb %>
4
+ <%# New nested @records format: each record has :details and optional :children %>
5
+ <%= render_entity_list_header(@entity_report_header_caption, "") %>
6
+ <% if defined?(@entity_filter_header_caption) && @entity_filter_header_caption.present? %>
7
+ <div class="entity-filter-header-caption" style="margin-top: 0.5rem; font-weight: normal;">
8
+ <%= @entity_filter_header_caption %>
9
+ </div>
10
+ <% end %>
11
+
12
+ <% if @records.any? %>
13
+ <!-- Added explicit borders for PDF rendering -->
14
+ <style>
15
+ .pdf-table th, .pdf-table td {
16
+ border: 1px solid #ccc;
17
+ padding: 8px;
18
+ }
19
+ .pdf-table {
20
+ border-collapse: collapse;
21
+ }
22
+
23
+ .text-end {
24
+ text-align: right;
25
+ font-weight: bold;
26
+ }
27
+
28
+ .fs-5 {
29
+ font-size: 1.25rem;
30
+ font-weight: bold;
31
+ }
32
+
33
+ .fs-6 {
34
+ font-size: 1rem;
35
+ font-weight: bold;
36
+ }
37
+
38
+ .fs-7 {
39
+ font-size: 0.875rem;
40
+ font-weight: bold;
41
+ }
42
+
43
+ .fs-8 {
44
+ font-size: 0.75rem;
45
+ font-weight: bold;
46
+ }
47
+
48
+ tr {
49
+ page-break-inside: avoid;
50
+ }
51
+
52
+ thead {
53
+ display: table-header-group; /* Ensures header repeats on each page */
54
+ }
55
+ </style>
56
+ <table class="table table-bordered table-striped table-hover text-muted pdf-table" style="width: 100%; border: 1px solid #000; border-collapse: collapse;">
57
+ <thead class="table-light">
58
+ <tr>
59
+ <% @records.first[:details].each_key do |key| %>
60
+ <% field_setting = @fields_settings.find { |f| f[:field] == key.to_s } %>
61
+
62
+ <th><%= field_setting && field_setting[:caption] ? field_setting[:caption] : key.to_s.titleize %></th>
63
+ <% end %>
64
+ </tr>
65
+ </thead>
66
+ <tbody>
67
+ <% @records.each do |record| %>
68
+ <%= render partial: 'lintity/entity_report/record', locals: { record: record } %>
69
+ <% end %>
70
+ </tbody>
71
+ <%# Calculate sums for numeric fields across all records and children %>
72
+ <% sums = Hash.new(0) %>
73
+ <% @records.each do |rec| %>
74
+ <% rec[:details].each do |k, v| %>
75
+ <% field_setting = @fields_settings.find { |f| f[:field] == k.to_s } %>
76
+ <% if field_setting && field_setting[:type] == "number" %>
77
+ <% sums[k] += v if v.is_a?(Numeric) %>
78
+ <% end %>
79
+ <% end %>
80
+ <% end %>
81
+ <tfoot>
82
+ <tr class="fw-bold">
83
+ <% @records.first[:details].each_key do |key| %>
84
+ <% field_setting = @fields_settings.find { |f| f[:field] == key.to_s } %>
85
+ <% if field_setting && field_setting[:type] == "number" %>
86
+ <td class="text-end"><%= sums[key] %></td>
87
+ <% else %>
88
+ <td></td>
89
+ <% end %>
90
+ <% end %>
91
+ </tr>
92
+ </tfoot>
93
+ </table>
94
+ <% else %>
95
+ <div class="alert alert-info">No records found for the selected filters.</div>
96
+ <% end %>
data/config/routes.rb CHANGED
@@ -1,3 +1,6 @@
1
1
  Lintity::Engine.routes.draw do
2
2
  get 'entity_list/index'
3
+ # Route for the new EntityReportController index action
4
+ # Allow HTML and PDF formats for the entity report index
5
+ get 'entity_report/index(.:format)', to: 'entity_report#index'
3
6
  end
@@ -1,3 +1,3 @@
1
1
  module Lintity
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lintity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fat Shinobi
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-06-08 00:00:00.000000000 Z
10
+ date: 2026-06-05 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
@@ -16,20 +15,20 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '7.0'
18
+ version: '8.0'
20
19
  - - ">="
21
20
  - !ruby/object:Gem::Version
22
- version: 7.0.2.3
21
+ version: 8.1.3
23
22
  type: :runtime
24
23
  prerelease: false
25
24
  version_requirements: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - "~>"
28
27
  - !ruby/object:Gem::Version
29
- version: '7.0'
28
+ version: '8.0'
30
29
  - - ">="
31
30
  - !ruby/object:Gem::Version
32
- version: 7.0.2.3
31
+ version: 8.1.3
33
32
  - !ruby/object:Gem::Dependency
34
33
  name: importmap-rails
35
34
  requirement: !ruby/object:Gem::Requirement
@@ -44,6 +43,48 @@ dependencies:
44
43
  - - ">="
45
44
  - !ruby/object:Gem::Version
46
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: wicked_pdf
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ - !ruby/object:Gem::Dependency
61
+ name: wkhtmltopdf-binary
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: pagy
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :runtime
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
47
88
  description: Rails gem to create list of entities
48
89
  email:
49
90
  - r3mnik@gmail.com
@@ -60,12 +101,17 @@ files:
60
101
  - app/assets/stylesheets/lintity/application.css
61
102
  - app/controllers/lintity/application_controller.rb
62
103
  - app/controllers/lintity/entity_list_controller.rb
104
+ - app/controllers/lintity/entity_report_controller.rb
63
105
  - app/helpers/lintity/application_helper.rb
64
106
  - app/jobs/lintity/application_job.rb
65
107
  - app/mailers/lintity/application_mailer.rb
66
108
  - app/models/lintity/application_record.rb
67
109
  - app/views/layouts/lintity/application.html.erb
68
110
  - app/views/lintity/entity_list/index.html.erb
111
+ - app/views/lintity/entity_report/_record.html.erb
112
+ - app/views/lintity/entity_report/_record.pdf.erb
113
+ - app/views/lintity/entity_report/index.html.erb
114
+ - app/views/lintity/entity_report/index.pdf.erb
69
115
  - config/importmap.rb
70
116
  - config/routes.rb
71
117
  - lib/lintity.rb
@@ -79,7 +125,6 @@ metadata:
79
125
  homepage_uri: https://github.com/fatshinobi/lintity
80
126
  source_code_uri: https://github.com/fatshinobi/lintity
81
127
  changelog_uri: https://github.com/fatshinobi/lintity
82
- post_install_message:
83
128
  rdoc_options: []
84
129
  require_paths:
85
130
  - lib
@@ -94,8 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
139
  - !ruby/object:Gem::Version
95
140
  version: '0'
96
141
  requirements: []
97
- rubygems_version: 3.2.3
98
- signing_key:
142
+ rubygems_version: 3.6.2
99
143
  specification_version: 4
100
144
  summary: Rails gem to create list of entities
101
145
  test_files: []