bidi2pdf-rails 0.0.1.pre.alpha

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.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.idea/.gitignore +8 -0
  3. data/.idea/bidi2pdf-rails.iml +411 -0
  4. data/.idea/inspectionProfiles/profiles_settings.xml +5 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/vcs.xml +6 -0
  8. data/.rspec +3 -0
  9. data/.rubocop.yml +90 -0
  10. data/.ruby-gemset +1 -0
  11. data/.ruby-version +1 -0
  12. data/MIT-LICENSE +20 -0
  13. data/README.md +75 -0
  14. data/Rakefile +11 -0
  15. data/lib/bidi2pdf-rails.rb +4 -0
  16. data/lib/bidi2pdf_rails/chromedriver_manager_singleton.rb +69 -0
  17. data/lib/bidi2pdf_rails/log_subscriber.rb +13 -0
  18. data/lib/bidi2pdf_rails/railtie.rb +79 -0
  19. data/lib/bidi2pdf_rails/version.rb +3 -0
  20. data/lib/bidi2pdf_rails.rb +87 -0
  21. data/lib/generators/bidi2pdf_rails/USAGE +8 -0
  22. data/lib/generators/bidi2pdf_rails/initializer_generator.rb +73 -0
  23. data/lib/generators/bidi2pdf_rails/templates/bidi2pdf_rails.rb.tt +89 -0
  24. data/spec/bidi2pdf_rails/bidi2pdf_rails_spec.rb +9 -0
  25. data/spec/dummy/Rakefile +6 -0
  26. data/spec/dummy/app/assets/images/img.jpg +0 -0
  27. data/spec/dummy/app/assets/javascripts/paged.polyfill-4.3.js +33251 -0
  28. data/spec/dummy/app/assets/stylesheets/application.css +1 -0
  29. data/spec/dummy/app/assets/stylesheets/pdf.css +121 -0
  30. data/spec/dummy/app/assets/stylesheets/reset.css +449 -0
  31. data/spec/dummy/app/assets/stylesheets/screen.css +281 -0
  32. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  33. data/spec/dummy/app/controllers/reports_controller.rb +8 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/helpers/reports_helper.rb +2 -0
  36. data/spec/dummy/app/views/layouts/application.html.erb +67 -0
  37. data/spec/dummy/app/views/layouts/pdf.html.erb +67 -0
  38. data/spec/dummy/app/views/pwa/manifest.json.erb +22 -0
  39. data/spec/dummy/app/views/pwa/service-worker.js +26 -0
  40. data/spec/dummy/app/views/reports/show.html.erb +153 -0
  41. data/spec/dummy/bin/rails +4 -0
  42. data/spec/dummy/bin/rake +4 -0
  43. data/spec/dummy/bin/setup +29 -0
  44. data/spec/dummy/config/application.rb +40 -0
  45. data/spec/dummy/config/boot.rb +5 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +57 -0
  48. data/spec/dummy/config/environments/production.rb +73 -0
  49. data/spec/dummy/config/environments/test.rb +51 -0
  50. data/spec/dummy/config/initializers/bidi2pdf_rails.rb +64 -0
  51. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  52. data/spec/dummy/config/initializers/cors.rb +9 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +8 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/permissions_policy.rb +13 -0
  56. data/spec/dummy/config/locales/en.yml +31 -0
  57. data/spec/dummy/config/puma.rb +34 -0
  58. data/spec/dummy/config/routes.rb +16 -0
  59. data/spec/dummy/config.ru +6 -0
  60. data/spec/dummy/log/development.log +220 -0
  61. data/spec/dummy/public/404.html +67 -0
  62. data/spec/dummy/public/406-unsupported-browser.html +66 -0
  63. data/spec/dummy/public/422.html +67 -0
  64. data/spec/dummy/public/500.html +66 -0
  65. data/spec/dummy/public/icon.png +0 -0
  66. data/spec/dummy/public/icon.svg +3 -0
  67. data/spec/dummy/spec/helpers/reports_helper_spec.rb +15 -0
  68. data/spec/dummy/spec/requests/reports_spec.rb +10 -0
  69. data/spec/dummy/spec/views/reports/show.html.erb_spec.rb +5 -0
  70. data/spec/dummy/storage/test.sqlite3 +0 -0
  71. data/spec/dummy/tmp/README.md +69 -0
  72. data/spec/dummy/tmp/local_secret.txt +1 -0
  73. data/spec/dummy/tmp/pids/server.pid +1 -0
  74. data/spec/dummy/tmp/restart.txt +0 -0
  75. data/spec/generator/bidie2pdf_rails_initializer_generator_spec.rb +5 -0
  76. data/spec/generator/initializer_generator_spec.rb +5 -0
  77. data/spec/rails_helper.rb +60 -0
  78. data/spec/requests/reports_spec.rb +17 -0
  79. data/spec/spec_helper.rb +94 -0
  80. data/tasks/bidi2pdf_rails_tasks.rake +4 -0
  81. metadata +289 -0
@@ -0,0 +1,281 @@
1
+ /* CSS for Paged.js interface – v0.4 */
2
+
3
+ /* Change the look */
4
+ :root {
5
+ --color-background: whitesmoke;
6
+ --color-pageSheet: #cfcfcf;
7
+ --color-pageBox: violet;
8
+ --color-paper: white;
9
+ --color-marginBox: transparent;
10
+ --pagedjs-crop-color: black;
11
+ --pagedjs-crop-shadow: white;
12
+ --pagedjs-crop-stroke: 1px;
13
+ }
14
+
15
+ /* To define how the book look on the screen: */
16
+ @media screen, pagedjs-ignore {
17
+ body {
18
+ background-color: var(--color-background);
19
+ }
20
+
21
+ .pagedjs_pages {
22
+ display: flex;
23
+ width: calc(var(--pagedjs-width) * 2);
24
+ flex: 0;
25
+ flex-wrap: wrap;
26
+ margin: 0 auto;
27
+ }
28
+
29
+ .pagedjs_page {
30
+ background-color: var(--color-paper);
31
+ box-shadow: 0 0 0 1px var(--color-pageSheet);
32
+ margin: 0;
33
+ flex-shrink: 0;
34
+ flex-grow: 0;
35
+ margin-top: 10mm;
36
+ }
37
+
38
+ .pagedjs_first_page {
39
+ margin-left: var(--pagedjs-width);
40
+ }
41
+
42
+ .pagedjs_page:last-of-type {
43
+ margin-bottom: 10mm;
44
+ }
45
+
46
+ .pagedjs_pagebox{
47
+ box-shadow: 0 0 0 1px var(--color-pageBox);
48
+ }
49
+
50
+ .pagedjs_left_page{
51
+ z-index: 20;
52
+ width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width))!important;
53
+ }
54
+
55
+ .pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop {
56
+ border-color: transparent;
57
+ }
58
+
59
+ .pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{
60
+ width: 0;
61
+ }
62
+
63
+ .pagedjs_right_page{
64
+ z-index: 10;
65
+ position: relative;
66
+ left: calc(var(--pagedjs-bleed-left)*-1);
67
+ }
68
+
69
+ /* show the margin-box */
70
+
71
+ .pagedjs_margin-top-left-corner-holder,
72
+ .pagedjs_margin-top,
73
+ .pagedjs_margin-top-left,
74
+ .pagedjs_margin-top-center,
75
+ .pagedjs_margin-top-right,
76
+ .pagedjs_margin-top-right-corner-holder,
77
+ .pagedjs_margin-bottom-left-corner-holder,
78
+ .pagedjs_margin-bottom,
79
+ .pagedjs_margin-bottom-left,
80
+ .pagedjs_margin-bottom-center,
81
+ .pagedjs_margin-bottom-right,
82
+ .pagedjs_margin-bottom-right-corner-holder,
83
+ .pagedjs_margin-right,
84
+ .pagedjs_margin-right-top,
85
+ .pagedjs_margin-right-middle,
86
+ .pagedjs_margin-right-bottom,
87
+ .pagedjs_margin-left,
88
+ .pagedjs_margin-left-top,
89
+ .pagedjs_margin-left-middle,
90
+ .pagedjs_margin-left-bottom {
91
+ box-shadow: 0 0 0 1px inset var(--color-marginBox);
92
+ }
93
+
94
+ /* uncomment this part for recto/verso book : ------------------------------------ */
95
+
96
+ /*
97
+ .pagedjs_pages {
98
+ flex-direction: column;
99
+ width: 100%;
100
+ }
101
+
102
+ .pagedjs_first_page {
103
+ margin-left: 0;
104
+ }
105
+
106
+ .pagedjs_page {
107
+ margin: 0 auto;
108
+ margin-top: 10mm;
109
+ }
110
+
111
+ .pagedjs_left_page{
112
+ width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width) + var(--pagedjs-bleed-left))!important;
113
+ }
114
+
115
+ .pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop{
116
+ border-color: var(--pagedjs-crop-color);
117
+ }
118
+
119
+ .pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{
120
+ width: var(--pagedjs-cross-size)!important;
121
+ }
122
+
123
+ .pagedjs_right_page{
124
+ left: 0;
125
+ }
126
+ */
127
+
128
+
129
+
130
+ /*--------------------------------------------------------------------------------------*/
131
+
132
+
133
+
134
+ /* uncomment this par to see the baseline : -------------------------------------------*/
135
+
136
+
137
+ /* .pagedjs_pagebox {
138
+ --pagedjs-baseline: 22px;
139
+ --pagedjs-baseline-position: 5px;
140
+ --pagedjs-baseline-color: cyan;
141
+ background: linear-gradient(transparent 0%, transparent calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) var(--pagedjs-baseline)), transparent;
142
+ background-size: 100% var(--pagedjs-baseline);
143
+ background-repeat: repeat-y;
144
+ background-position-y: var(--pagedjs-baseline-position);
145
+ } */
146
+
147
+
148
+ /*--------------------------------------------------------------------------------------*/
149
+ }
150
+
151
+
152
+
153
+
154
+
155
+ /* Marks (to delete when merge in paged.js) */
156
+
157
+ .pagedjs_marks-crop{
158
+ z-index: 999999999999;
159
+
160
+ }
161
+
162
+ .pagedjs_bleed-top .pagedjs_marks-crop,
163
+ .pagedjs_bleed-bottom .pagedjs_marks-crop{
164
+ box-shadow: 1px 0px 0px 0px var(--pagedjs-crop-shadow);
165
+ }
166
+
167
+ .pagedjs_bleed-top .pagedjs_marks-crop:last-child,
168
+ .pagedjs_bleed-bottom .pagedjs_marks-crop:last-child{
169
+ box-shadow: -1px 0px 0px 0px var(--pagedjs-crop-shadow);
170
+ }
171
+
172
+ .pagedjs_bleed-left .pagedjs_marks-crop,
173
+ .pagedjs_bleed-right .pagedjs_marks-crop{
174
+ box-shadow: 0px 1px 0px 0px var(--pagedjs-crop-shadow);
175
+ }
176
+
177
+ .pagedjs_bleed-left .pagedjs_marks-crop:last-child,
178
+ .pagedjs_bleed-right .pagedjs_marks-crop:last-child{
179
+ box-shadow: 0px -1px 0px 0px var(--pagedjs-crop-shadow);
180
+ }
181
+
182
+ body{
183
+ --header-w-close: 78px;
184
+ --header-w-open: 220px;
185
+ --color-icon-select: rgb(8, 38, 92);
186
+ --color-icon: rgb(85, 109, 175);
187
+ }
188
+
189
+
190
+ #header-pagedjs{
191
+ display: none;
192
+ }
193
+
194
+
195
+
196
+ #header-pagedjs{
197
+ font-family: Arial, Helvetica, sans-serif;
198
+ position: fixed;
199
+ box-sizing: border-box;
200
+ top: 10px;
201
+ right: 40px;
202
+ z-index: 999;
203
+ }
204
+
205
+
206
+
207
+
208
+ #header-pagedjs button {
209
+ display: flex;
210
+ width: 48px;
211
+ height: 48px;
212
+ background-color: transparent;
213
+ border: none;
214
+ align-items: center;
215
+ justify-content: flex-start;
216
+ margin-top: 18px;
217
+ margin-bottom: 18px;
218
+ cursor: pointer;
219
+ color: var(--color-icon);
220
+ background-color: rgb(225, 225, 225);
221
+ border: 3px solid rgb(225, 225, 225);
222
+ border-radius: 10px;
223
+
224
+ }
225
+
226
+
227
+ #header-pagedjs button svg {
228
+ width: 100%;
229
+ fill: var(--color-icon);
230
+ }
231
+
232
+
233
+ #header-pagedjs button::before{
234
+ content: attr(data-text);
235
+ color: var(--color-icon-select)!important;
236
+ position: absolute;
237
+ width: 200px;
238
+ left: -216px;
239
+ text-align: right;
240
+ font-size: 18px;
241
+ display: none;
242
+ font-weight: bold;
243
+ /* background-color: rgba(225, 225, 225, 0.3);
244
+ padding: 3px 8px; */
245
+ }
246
+
247
+ /* checked */
248
+ #input-screen:checked ~ #button-screen,
249
+ #input-print:checked ~ #button-print-preview { border: 3px solid var(--color-icon); }
250
+
251
+ /* disabled */
252
+ button:disabled { cursor: none; }
253
+ #button-print:disabled svg{ cursor: none; fill: #bbb!important; }
254
+
255
+ /* hover */
256
+ #header-pagedjs button:hover svg{ fill: var(--color-icon-select); }
257
+ #header-pagedjs button:hover{ border: 3px solid var(--color-icon-select)!important; }
258
+ #header-pagedjs button:hover::before{ display: block!important; }
259
+
260
+
261
+ @media screen, pagedjs-ignore {
262
+ #header-pagedjs{ display: block; }
263
+ }
264
+
265
+ @media screen, pagedjs-ignore {
266
+
267
+ .pagedjs_pages {
268
+ flex-direction: column;
269
+ width: 100%;
270
+ }
271
+
272
+ .pagedjs_first_page {
273
+ margin-left: 0;
274
+ }
275
+
276
+ .pagedjs_page {
277
+ margin: 0 auto;
278
+ margin-top: 10mm;
279
+ }
280
+
281
+ }
@@ -0,0 +1,4 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
3
+ allow_browser versions: :modern
4
+ end
@@ -0,0 +1,8 @@
1
+ class ReportsController < ApplicationController
2
+ def show
3
+ respond_to do |format|
4
+ format.html
5
+ format.pdf { render pdf: 'my-report', layout: 'pdf' }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ module ReportsHelper
2
+ end
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= content_for(:title) || "Dummy" %></title>
5
+ <%= csp_meta_tag %>
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <%= csrf_meta_tags %>
9
+ <%= csp_meta_tag %>
10
+
11
+ <%= yield :head %>
12
+
13
+ <link rel="manifest" href="/manifest.json">
14
+ <link rel="icon" href="/icon.png" type="image/png">
15
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
16
+ <link rel="apple-touch-icon" href="/icon.png">
17
+
18
+ <%= stylesheet_link_tag "stylesheets/reset", nonce: true %>
19
+ <%= stylesheet_link_tag "stylesheets/screen", nonce: true %>
20
+ <%= stylesheet_link_tag "stylesheets/pdf", nonce: true %>
21
+
22
+ <script>
23
+ window.loaded = false; // make it global
24
+
25
+ window.PagedConfig = {
26
+ auto: false,
27
+ after: (flow) => { },
28
+ };
29
+
30
+ function initPaged() {
31
+ console.log("Paged library is ready");
32
+ // Initialize your Paged handlers here
33
+
34
+ class PageJsReadyHandler extends Paged.Handler {
35
+ constructor(chunker, polisher, caller) {
36
+ super(chunker, polisher, caller);
37
+ }
38
+
39
+ afterRendered(pages) {
40
+ window.loaded = true;
41
+ console.info("✅ Paged.js has rendered everything!");
42
+ }
43
+ }
44
+
45
+ Paged.registerHandlers(PageJsReadyHandler);
46
+
47
+ window.PagedPolyfill.preview();
48
+ }
49
+ </script>
50
+
51
+ <%= javascript_include_tag "javascripts/paged.polyfill-4.3.js", nonce: true, async: true, onload: "initPaged()" %>
52
+
53
+ <!-- No Favicon -->
54
+ <link rel="icon" href="data:,">
55
+
56
+ <link
57
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
58
+ rel="stylesheet"
59
+ crossorigin="anonymous"
60
+ />
61
+
62
+ </head>
63
+
64
+ <body>
65
+ <%= yield %>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= content_for(:title) || "Dummy" %></title>
5
+ <%= csp_meta_tag %>
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <%= csrf_meta_tags %>
9
+ <%= csp_meta_tag %>
10
+
11
+ <%= yield :head %>
12
+
13
+ <link rel="manifest" href="/manifest.json">
14
+ <link rel="icon" href="/icon.png" type="image/png">
15
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
16
+ <link rel="apple-touch-icon" href="/icon.png">
17
+
18
+ <%= stylesheet_link_tag "stylesheets/reset", nonce: true %>
19
+ <%= stylesheet_link_tag "stylesheets/screen", nonce: true %>
20
+ <%= stylesheet_link_tag "stylesheets/pdf", nonce: true %>
21
+
22
+ <script>
23
+ window.loaded = false; // make it global
24
+
25
+ window.PagedConfig = {
26
+ auto: false,
27
+ after: (flow) => { },
28
+ };
29
+
30
+ function initPaged() {
31
+ console.log("Paged library is ready");
32
+ // Initialize your Paged handlers here
33
+
34
+ class PageJsReadyHandler extends Paged.Handler {
35
+ constructor(chunker, polisher, caller) {
36
+ super(chunker, polisher, caller);
37
+ }
38
+
39
+ afterRendered(pages) {
40
+ window.loaded = true;
41
+ console.info("✅ Paged.js has rendered everything!");
42
+ }
43
+ }
44
+
45
+ Paged.registerHandlers(PageJsReadyHandler);
46
+
47
+ window.PagedPolyfill.preview();
48
+ }
49
+ </script>
50
+
51
+ <%= javascript_include_tag "javascripts/paged.polyfill-4.3.js", nonce: true, async: true, onload: "initPaged()" %>
52
+
53
+ <!-- No Favicon -->
54
+ <link rel="icon" href="data:,">
55
+
56
+ <link
57
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
58
+ rel="stylesheet"
59
+ crossorigin="anonymous"
60
+ />
61
+
62
+ </head>
63
+
64
+ <body>
65
+ <%= yield %>
66
+ </body>
67
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "Dummy",
3
+ "icons": [
4
+ {
5
+ "src": "/icon.png",
6
+ "type": "image/png",
7
+ "sizes": "512x512"
8
+ },
9
+ {
10
+ "src": "/icon.png",
11
+ "type": "image/png",
12
+ "sizes": "512x512",
13
+ "purpose": "maskable"
14
+ }
15
+ ],
16
+ "start_url": "/",
17
+ "display": "standalone",
18
+ "scope": "/",
19
+ "description": "Dummy.",
20
+ "theme_color": "red",
21
+ "background_color": "red"
22
+ }
@@ -0,0 +1,26 @@
1
+ // Add a service worker for processing Web Push notifications:
2
+ //
3
+ // self.addEventListener("push", async (event) => {
4
+ // const { title, options } = await event.data.json()
5
+ // event.waitUntil(self.registration.showNotification(title, options))
6
+ // })
7
+ //
8
+ // self.addEventListener("notificationclick", function(event) {
9
+ // event.notification.close()
10
+ // event.waitUntil(
11
+ // clients.matchAll({ type: "window" }).then((clientList) => {
12
+ // for (let i = 0; i < clientList.length; i++) {
13
+ // let client = clientList[i]
14
+ // let clientPath = (new URL(client.url)).pathname
15
+ //
16
+ // if (clientPath == event.notification.data.path && "focus" in client) {
17
+ // return client.focus()
18
+ // }
19
+ // }
20
+ //
21
+ // if (clients.openWindow) {
22
+ // return clients.openWindow(event.notification.data.path)
23
+ // }
24
+ // })
25
+ // )
26
+ // })
@@ -0,0 +1,153 @@
1
+ <header class="page-header">
2
+ <h1>PDF Rendering Sample</h1>
3
+ <p class="text-muted">Styled with Bootstrap &amp; custom CSS</p>
4
+ </header>
5
+ <!-- don't know why, but it has to be placed here -->
6
+ <footer class="page-footer text-center">
7
+ <p class="small text-muted">Generated with ❤️ by Bidi2PDF – Page </p>
8
+ </footer>
9
+
10
+
11
+ <main class="page-content container mt-4">
12
+ <section>
13
+ <h2 class="section-heading">Section One</h2>
14
+ <p>This is the first page. Add enough text here to push to the next page.</p>
15
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
16
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
17
+ bibendum.</p>
18
+ <p>Vestibulum quis velit at augue tincidunt egestas. Integer non dolor a nisl consequat gravida. Sed vitae
19
+ fermentum sapien. Suspendisse potenti.</p>
20
+ <p class="break-after"></p>
21
+ </section>
22
+ <section>
23
+ <h2 class="section-heading">Section Two (New Page)</h2>
24
+ <p>This content appears on a second page.</p>
25
+
26
+ <h3>Sample Table</h3>
27
+ <table class="table table-bordered">
28
+ <thead class="table-light">
29
+ <tr>
30
+ <th>#</th>
31
+ <th>Item</th>
32
+ <th>Description</th>
33
+ <th>Qty</th>
34
+ </tr>
35
+ </thead>
36
+ <tbody>
37
+ <tr>
38
+ <td>1</td>
39
+ <td>Widget A</td>
40
+ <td>High-quality widget</td>
41
+ <td>2</td>
42
+ </tr>
43
+ <tr>
44
+ <td>2</td>
45
+ <td>Widget B</td>
46
+ <td>Durable widget for long-term use</td>
47
+ <td>1</td>
48
+ </tr>
49
+ <tr>
50
+ <td>3</td>
51
+ <td>Gadget C</td>
52
+ <td>Great for productivity</td>
53
+ <td>3</td>
54
+ </tr>
55
+ <tr>
56
+ <td>4</td>
57
+ <td>Thingamajig D</td>
58
+ <td>Essential thingy</td>
59
+ <td>4</td>
60
+ </tr>
61
+ <tr>
62
+ <td>5</td>
63
+ <td>Whatchamacallit E</td>
64
+ <td>Mysterious and useful</td>
65
+ <td>1</td>
66
+ </tr>
67
+ </tbody>
68
+ </table>
69
+ <p class="content">
70
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
71
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
72
+ bibendum.
73
+ </p>
74
+ <p class="content">
75
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
76
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
77
+ bibendum.
78
+ </p>
79
+
80
+ <div class="float-start">
81
+ <%= image_tag 'images/img.jpg', class: "img-fluid my-3 me-3", style: "max-width:10cm;" %>
82
+ </div>
83
+
84
+ <div class="float-end">
85
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus.</p>
86
+ <p>Vestibulum quis velit at augue tincidunt egestas.</p>
87
+ </div>
88
+
89
+ <div class="clearfix"></div>
90
+
91
+ <blockquote class="blockquote border-start border-primary ps-3 my-4">
92
+ <p>"Good design is obvious. Great design is transparent."</p>
93
+ <footer class="blockquote-footer">Joe Sparano</footer>
94
+ </blockquote>
95
+
96
+ <div class="alert alert-info my-4" role="alert">
97
+ <strong>Note:</strong> This document demonstrates advanced pagination with HTML and CSS.
98
+ </div>
99
+
100
+ <svg width="120" height="120" viewBox="0 0 32 32" class="my-4">
101
+ <circle r="16" cx="16" cy="16" fill="#f3f3f3"/>
102
+ <circle r="16" cx="16" cy="16" fill="transparent"
103
+ stroke="#4a90e2" stroke-width="32" stroke-dasharray="75 25"
104
+ transform="rotate(-90) translate(-32)"/>
105
+ <text x="16" y="20" text-anchor="middle" fill="#333" font-size="5">75%</text>
106
+ </svg>
107
+
108
+ <div class="row my-4">
109
+ <div class="col-md-6">
110
+ <h4>Column One</h4>
111
+ <p>Lorem ipsum dolor sit amet consectetur adipiscing elit.</p>
112
+ </div>
113
+ <div class="col-md-6">
114
+ <h4>Column Two</h4>
115
+ <p>Suspendisse potenti. Nullam eget neque id sem laoreet convallis.</p>
116
+ </div>
117
+ </div>
118
+
119
+ <p class="content">
120
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
121
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
122
+ bibendum.
123
+ </p>
124
+ <p class="content">
125
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
126
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
127
+ bibendum.
128
+ </p>
129
+ <p class="content">
130
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
131
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
132
+ bibendum.
133
+ </p>
134
+ <p class="content">
135
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
136
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
137
+ bibendum.
138
+ </p>
139
+ <p class="content">
140
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a ullamcorper purus. Nulla facilisi.
141
+ Cras pretium risus ac ligula dignissim, ut dictum justo fermentum. Donec dapibus purus vitae malesuada
142
+ bibendum.
143
+ </p>
144
+ </section>
145
+ <section class="signature-section my-5 pt-5 text-center">
146
+ <p>Prepared by:</p>
147
+ <p><strong>Your Name or Company</strong></p>
148
+ <div class="border-top w-25 mx-auto my-2"></div>
149
+ <p class="small text-muted">Signature</p>
150
+ </section>
151
+ </main>
152
+
153
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ APP_ROOT = File.expand_path("..", __dir__)
5
+ APP_NAME = "dummy"
6
+
7
+ def system!(*args)
8
+ system(*args, exception: true)
9
+ end
10
+
11
+ FileUtils.chdir APP_ROOT do
12
+ # This script is a way to set up or update your development environment automatically.
13
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14
+ # Add necessary setup steps to this file.
15
+
16
+ puts "== Installing dependencies =="
17
+ system! "gem install bundler --conservative"
18
+ system("bundle check") || system!("bundle install")
19
+
20
+ puts "\n== Removing old logs and tempfiles =="
21
+ system! "bin/rails log:clear tmp:clear"
22
+
23
+ puts "\n== Restarting application server =="
24
+ system! "bin/rails restart"
25
+
26
+ # puts "\n== Configuring puma-dev =="
27
+ # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
28
+ # system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
29
+ end