cafe_car 0.1.0 → 0.1.2

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +682 -8
  3. data/Rakefile +21 -0
  4. data/app/assets/fonts/Lexend.css +7 -0
  5. data/app/assets/fonts/Lexend.ttf +0 -0
  6. data/app/assets/images/noise.svg +16 -0
  7. data/app/assets/stylesheets/actiontext.css +31 -0
  8. data/app/assets/stylesheets/application.css +1 -0
  9. data/app/assets/stylesheets/cafe_car/code/base16-dark.css +89 -0
  10. data/app/assets/stylesheets/cafe_car/code/base16-light.css +90 -0
  11. data/app/assets/stylesheets/cafe_car/pagination.css +5 -0
  12. data/app/assets/stylesheets/cafe_car/themes/cool.css +32 -0
  13. data/app/assets/stylesheets/cafe_car/themes/cool2.css +31 -0
  14. data/app/assets/stylesheets/cafe_car/themes/defaults.css +60 -0
  15. data/app/assets/stylesheets/cafe_car/themes/warm-dark.css +29 -0
  16. data/app/assets/stylesheets/cafe_car/themes/warm.css +24 -0
  17. data/app/assets/stylesheets/cafe_car/tooltips.css +20 -0
  18. data/app/assets/stylesheets/cafe_car/trix.css +56 -0
  19. data/app/assets/stylesheets/cafe_car/utility.css +63 -0
  20. data/app/assets/stylesheets/cafe_car.css +96 -0
  21. data/app/assets/stylesheets/iconoir.css +22 -0
  22. data/app/assets/stylesheets/ui/Alert.css +25 -0
  23. data/app/assets/stylesheets/ui/Article.css +11 -0
  24. data/app/assets/stylesheets/ui/Button.css +42 -0
  25. data/app/assets/stylesheets/ui/Card.css +74 -0
  26. data/app/assets/stylesheets/ui/Chat.css +33 -0
  27. data/app/assets/stylesheets/ui/Close.css +11 -0
  28. data/app/assets/stylesheets/ui/Code.css +4 -0
  29. data/app/assets/stylesheets/ui/Controls.css +16 -0
  30. data/app/assets/stylesheets/ui/Error.css +3 -0
  31. data/app/assets/stylesheets/ui/Example.css +45 -0
  32. data/app/assets/stylesheets/ui/Field.css +31 -0
  33. data/app/assets/stylesheets/ui/Grid.css +6 -0
  34. data/app/assets/stylesheets/ui/Group.css +16 -0
  35. data/app/assets/stylesheets/ui/Icon.css +27 -0
  36. data/app/assets/stylesheets/ui/Image.css +14 -0
  37. data/app/assets/stylesheets/ui/InfoCircle.css +11 -0
  38. data/app/assets/stylesheets/ui/Input.css +36 -0
  39. data/app/assets/stylesheets/ui/Layout.css +100 -0
  40. data/app/assets/stylesheets/ui/Menu.css +38 -0
  41. data/app/assets/stylesheets/ui/Modal.css +26 -0
  42. data/app/assets/stylesheets/ui/Navigation.css +37 -0
  43. data/app/assets/stylesheets/ui/Page.css +105 -0
  44. data/app/assets/stylesheets/ui/Row.css +9 -0
  45. data/app/assets/stylesheets/ui/Table.css +101 -0
  46. data/app/assets/stylesheets/ui/components.css +24 -0
  47. data/app/controllers/cafe_car/application_controller.rb +9 -0
  48. data/app/controllers/cafe_car/examples_controller.rb +22 -0
  49. data/app/controllers/cafe_car/sessions_controller.rb +30 -0
  50. data/app/controllers/concerns/cafe_car/authentication.rb +61 -0
  51. data/app/javascript/application.js +5 -0
  52. data/app/javascript/cafe_car.js +174 -0
  53. data/app/models/cafe_car/session.rb +18 -0
  54. data/app/policies/cafe_car/application_policy.rb +42 -0
  55. data/app/policies/cafe_car/session_policy.rb +19 -0
  56. data/app/presenters/cafe_car/action_text/rich_text_presenter.rb +7 -0
  57. data/app/presenters/cafe_car/active_record/base_presenter.rb +6 -0
  58. data/app/presenters/cafe_car/active_record/relation_presenter.rb +17 -0
  59. data/app/presenters/cafe_car/active_storage/attached/one_presenter.rb +9 -0
  60. data/app/presenters/cafe_car/active_storage/attachment_presenter.rb +18 -0
  61. data/app/presenters/cafe_car/basic_object_presenter.rb +5 -0
  62. data/app/presenters/cafe_car/code_presenter.rb +18 -0
  63. data/app/presenters/cafe_car/currency_presenter.rb +5 -0
  64. data/app/presenters/cafe_car/date_and_time/compatibility_presenter.rb +6 -0
  65. data/app/presenters/cafe_car/date_presenter.rb +5 -0
  66. data/app/presenters/cafe_car/date_time_presenter.rb +11 -0
  67. data/app/presenters/cafe_car/enumerable_presenter.rb +13 -0
  68. data/app/presenters/cafe_car/false_class_presenter.rb +5 -0
  69. data/app/presenters/cafe_car/hash_presenter.rb +6 -0
  70. data/app/presenters/cafe_car/nil_class_presenter.rb +13 -0
  71. data/app/presenters/cafe_car/presenter.rb +157 -0
  72. data/app/presenters/cafe_car/range_presenter.rb +16 -0
  73. data/app/presenters/cafe_car/record_presenter.rb +5 -0
  74. data/app/presenters/cafe_car/string_presenter.rb +20 -0
  75. data/app/presenters/cafe_car/symbol_presenter.rb +5 -0
  76. data/app/presenters/cafe_car/true_class_presenter.rb +5 -0
  77. data/app/ui/cafe_car/ui/button.rb +9 -0
  78. data/app/ui/cafe_car/ui/card.rb +18 -0
  79. data/app/ui/cafe_car/ui/field.rb +11 -0
  80. data/app/ui/cafe_car/ui/grid.rb +30 -0
  81. data/app/ui/cafe_car/ui/layout.rb +7 -0
  82. data/app/ui/cafe_car/ui/page.rb +14 -0
  83. data/app/views/application/_actions.html.haml +1 -0
  84. data/app/views/application/_alerts.html.haml +2 -0
  85. data/app/views/application/_body.html.haml +7 -0
  86. data/app/views/application/_controls.html.haml +12 -0
  87. data/app/views/application/_debug.html.haml +18 -0
  88. data/app/views/application/_empty.html.haml +1 -0
  89. data/app/views/application/_errors.html.haml +4 -0
  90. data/app/views/application/_field.html.haml +5 -0
  91. data/app/views/application/_fields.html.haml +1 -0
  92. data/app/views/application/_filters.html.haml +8 -0
  93. data/app/views/application/_form.html.haml +6 -0
  94. data/app/views/application/_grid.html.haml +3 -0
  95. data/app/views/application/_grid_item.html.haml +1 -0
  96. data/app/views/application/_head.html.haml +17 -0
  97. data/app/views/application/_index.html.haml +8 -0
  98. data/app/views/application/_index_actions.html.haml +7 -0
  99. data/app/views/application/_navigation.html.haml +9 -0
  100. data/app/views/application/_navigation_links.html.haml +5 -0
  101. data/app/views/application/_notes.html.haml +10 -0
  102. data/app/views/application/_popup.html.haml +7 -0
  103. data/app/views/application/_show.html.haml +9 -0
  104. data/app/views/application/_submit.html.haml +1 -0
  105. data/app/views/application/_table.html.haml +6 -0
  106. data/app/views/cafe_car/application/create.turbo_stream.haml +2 -0
  107. data/app/views/cafe_car/application/destroy.turbo_stream.haml +1 -0
  108. data/app/views/cafe_car/application/edit.html.haml +18 -0
  109. data/app/views/cafe_car/application/edit.turbo_stream.haml +7 -0
  110. data/app/views/cafe_car/application/index.html.haml +15 -0
  111. data/app/views/cafe_car/application/new.html.haml +8 -0
  112. data/app/views/cafe_car/application/new.turbo_stream.haml +8 -0
  113. data/app/views/cafe_car/application/show.html.haml +36 -0
  114. data/app/views/cafe_car/application/update.turbo_stream.haml +2 -0
  115. data/app/views/cafe_car/examples/_example.html.haml +12 -0
  116. data/app/views/cafe_car/examples/_index.html.haml +16 -0
  117. data/app/views/cafe_car/examples/_navigation_links.html.haml +2 -0
  118. data/app/views/cafe_car/examples/ui/_alert.html.haml +4 -0
  119. data/app/views/cafe_car/examples/ui/_button.html.haml +3 -0
  120. data/app/views/cafe_car/examples/ui/_card.html.haml +6 -0
  121. data/app/views/cafe_car/examples/ui/_chat.html.haml +3 -0
  122. data/app/views/cafe_car/examples/ui/_controls.html.haml +3 -0
  123. data/app/views/cafe_car/examples/ui/_error.html.haml +1 -0
  124. data/app/views/cafe_car/examples/ui/_field.html.haml +9 -0
  125. data/app/views/cafe_car/examples/ui/_grid.html.haml +11 -0
  126. data/app/views/cafe_car/examples/ui/_group.html.haml +21 -0
  127. data/app/views/cafe_car/examples/ui/_info_circle.html.haml +1 -0
  128. data/app/views/cafe_car/examples/ui/_menu.html.haml +5 -0
  129. data/app/views/cafe_car/examples/ui/_modal.html.haml +4 -0
  130. data/app/views/cafe_car/examples/ui/_navigation.html.haml +4 -0
  131. data/app/views/cafe_car/examples/ui/_page.html.haml +4 -0
  132. data/app/views/cafe_car/examples/ui/_table.html.haml +13 -0
  133. data/app/views/cafe_car/layouts/mailer.html.haml +8 -0
  134. data/app/views/cafe_car/layouts/mailer.text.erb +1 -0
  135. data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
  136. data/app/views/layouts/application.html.haml +4 -0
  137. data/app/views/layouts/mailer.html.erb +13 -0
  138. data/app/views/layouts/mailer.text.erb +1 -0
  139. data/app/views/notes/_fields.html.haml +1 -0
  140. data/app/views/passwords_mailer/reset.html.haml +5 -0
  141. data/app/views/passwords_mailer/reset.text.erb +4 -0
  142. data/app/views/ui/_card.html.haml +8 -0
  143. data/app/views/ui/_field.html.haml +1 -0
  144. data/app/views/ui/_modal_close.html.haml +1 -0
  145. data/app/views/ui/_page.html.haml +7 -0
  146. data/config/brakeman.ignore +77 -0
  147. data/config/importmap.rb +12 -0
  148. data/config/locales/en.yml +63 -0
  149. data/config/routes.rb +9 -0
  150. data/db/migrate/20251005220017_create_slugs.rb +13 -0
  151. data/lib/cafe_car/active_record.rb +21 -0
  152. data/lib/cafe_car/application_responder.rb +17 -0
  153. data/lib/cafe_car/attributes.rb +23 -0
  154. data/lib/cafe_car/auto_resolver.rb +49 -0
  155. data/lib/cafe_car/caching.rb +20 -0
  156. data/lib/cafe_car/component.rb +155 -0
  157. data/lib/cafe_car/context.rb +17 -0
  158. data/lib/cafe_car/controller/filtering.rb +30 -0
  159. data/lib/cafe_car/controller.rb +218 -0
  160. data/lib/cafe_car/core_ext/array.rb +24 -0
  161. data/lib/cafe_car/core_ext/hash.rb +15 -0
  162. data/lib/cafe_car/core_ext/module.rb +15 -0
  163. data/lib/cafe_car/core_ext.rb +5 -0
  164. data/lib/cafe_car/current.rb +9 -0
  165. data/lib/cafe_car/engine.rb +107 -0
  166. data/lib/cafe_car/field_builder.rb +44 -0
  167. data/lib/cafe_car/field_info.rb +144 -0
  168. data/lib/cafe_car/fields.rb +21 -0
  169. data/lib/cafe_car/filter/field_builder.rb +4 -0
  170. data/lib/cafe_car/filter/field_info.rb +22 -0
  171. data/lib/cafe_car/filter/form_builder.rb +21 -0
  172. data/lib/cafe_car/filter.rb +5 -0
  173. data/lib/cafe_car/filter_builder.rb +20 -0
  174. data/lib/cafe_car/form_builder.rb +105 -0
  175. data/lib/cafe_car/generators.rb +30 -0
  176. data/lib/cafe_car/helpers.rb +178 -0
  177. data/lib/cafe_car/href_builder.rb +97 -0
  178. data/lib/cafe_car/informable.rb +9 -0
  179. data/lib/cafe_car/input_builder.rb +25 -0
  180. data/lib/cafe_car/inputs/association_builder.rb +6 -0
  181. data/lib/cafe_car/inputs/base_input.rb +19 -0
  182. data/lib/cafe_car/inputs/belongs_to_builder.rb +6 -0
  183. data/lib/cafe_car/inputs/password_input.rb +7 -0
  184. data/lib/cafe_car/inputs/string_input.rb +7 -0
  185. data/lib/cafe_car/link_builder.rb +65 -0
  186. data/lib/cafe_car/model.rb +23 -0
  187. data/lib/cafe_car/model_info.rb +24 -0
  188. data/lib/cafe_car/name_patch.rb +17 -0
  189. data/lib/cafe_car/navigation.rb +76 -0
  190. data/lib/cafe_car/option_helpers.rb +53 -0
  191. data/lib/cafe_car/param_parser.rb +45 -0
  192. data/lib/cafe_car/pluralization.rb +15 -0
  193. data/lib/cafe_car/policy.rb +77 -0
  194. data/lib/cafe_car/proc_helpers.rb +13 -0
  195. data/lib/cafe_car/query_builder.rb +186 -0
  196. data/lib/cafe_car/queryable.rb +29 -0
  197. data/lib/cafe_car/resolver.rb +27 -0
  198. data/lib/cafe_car/routing.rb +17 -0
  199. data/lib/cafe_car/table/body_builder.rb +12 -0
  200. data/lib/cafe_car/table/builder.rb +52 -0
  201. data/lib/cafe_car/table/foot_builder.rb +14 -0
  202. data/lib/cafe_car/table/head_builder.rb +26 -0
  203. data/lib/cafe_car/table/label_builder.rb +48 -0
  204. data/lib/cafe_car/table/objects_builder.rb +8 -0
  205. data/lib/cafe_car/table/row_builder.rb +39 -0
  206. data/lib/cafe_car/table_builder.rb +13 -0
  207. data/lib/cafe_car/turbo_tag_builder.rb +7 -0
  208. data/lib/cafe_car/ui.rb +11 -0
  209. data/lib/cafe_car/version.rb +1 -1
  210. data/lib/cafe_car/visitors.rb +21 -0
  211. data/lib/cafe_car.rb +25 -168
  212. data/lib/generators/cafe_car/controller/USAGE +11 -0
  213. data/lib/generators/cafe_car/controller/controller_generator.rb +26 -0
  214. data/lib/generators/cafe_car/controller/templates/controller.rb.tt +5 -0
  215. data/lib/generators/cafe_car/install/USAGE +8 -0
  216. data/lib/generators/cafe_car/install/install_generator.rb +46 -0
  217. data/lib/generators/cafe_car/install/templates/application_policy.rb.tt +7 -0
  218. data/lib/generators/cafe_car/notes/USAGE +12 -0
  219. data/lib/generators/cafe_car/notes/notes_generator.rb +13 -0
  220. data/lib/generators/cafe_car/notes/templates/create_notes.rb.tt +12 -0
  221. data/lib/generators/cafe_car/notes/templates/notable.rb.tt +7 -0
  222. data/lib/generators/cafe_car/notes/templates/note.rb.tt +6 -0
  223. data/lib/generators/cafe_car/policy/USAGE +8 -0
  224. data/lib/generators/cafe_car/policy/policy_generator.rb +39 -0
  225. data/lib/generators/cafe_car/policy/templates/policy.rb.tt +20 -0
  226. data/lib/generators/cafe_car/resource/USAGE +13 -0
  227. data/lib/generators/cafe_car/resource/resource_generator.rb +32 -0
  228. data/lib/generators/cafe_car/sessions/USAGE +17 -0
  229. data/lib/generators/cafe_car/sessions/sessions_generator.rb +29 -0
  230. data/lib/generators/cafe_car/sessions/templates/create_sessions.rb.tt +12 -0
  231. data/lib/tasks/holdco_tasks.rake +532 -0
  232. data/lib/tasks/templates/tasks_header.md +37 -0
  233. metadata +444 -21
  234. data/app/views/cafe_car/application/_fields.html.erb +0 -7
  235. data/app/views/cafe_car/application/_filters.html.erb +0 -0
  236. data/app/views/cafe_car/application/_form.html.erb +0 -22
  237. data/lib/cafe_car/railtie.rb +0 -4
  238. /data/app/views/{cafe_car/application/_actions.html.erb → application/_aside.html.haml} +0 -0
  239. /data/app/views/{cafe_car/application/_aside.html.erb → application/_footer.html.haml} +0 -0
  240. /data/app/views/cafe_car/{application/_extra_fields.html.erb → examples/_index_actions.html.haml} +0 -0
@@ -0,0 +1,8 @@
1
+ - title = breadcrumbs(link(model).index, title("New #{model_name.human}"))
2
+
3
+ = Page :slim, title: do |page|
4
+ = page.Body do
5
+ = render "alerts"
6
+ = Card do |card|
7
+ = render "form", card:
8
+ = render "debug" if debug?
@@ -0,0 +1,8 @@
1
+ - if action.new?
2
+ = render "popup" do
3
+ = Card title: "New #{model_name.human}", actions: Close { icon(:xmark_circle_solid) } do |card|
4
+ = render "form", card:
5
+
6
+ - else # error during :create action
7
+ = turbo_stream.replace(dom_id(object, :new)) do
8
+ = render "form", card: ui.Card
@@ -0,0 +1,36 @@
1
+ - title = breadcrumbs(link(object).index, title(object))
2
+ - summary = ui(object).timestamps try: true
3
+ - body = render("show")
4
+
5
+ = turbo_stream_from(object) if object.persisted?
6
+ = Page :full, title: do |page|
7
+ = page.Aside do
8
+ = yield :left
9
+
10
+ = page.Body do
11
+ = render "alerts"
12
+
13
+ - if tab = params[:tab]
14
+ -# - title tab.humanize
15
+ -# = row bottom: true do
16
+ -# = page.head
17
+ -# %h1 object.try(:name).fmap {|name| "#{name}'s #{tab.humanize(capitalize: false)}" }.felse { tab.humanize }
18
+ -# = link "← Back to #{model_name.human}", link: path_super(object)
19
+ -#
20
+ -# = render "tab_#{tab}"
21
+ - else
22
+ = body
23
+ = render "debug" if debug?
24
+
25
+ = page.Aside do
26
+ = Card :sticky do |card|
27
+ = card.Section ui(object).controls
28
+ = card.Section summary
29
+
30
+ = Card title: "Actions" do |card|
31
+ = render("actions", card:)
32
+
33
+ = Card title: "Notes" do |card|
34
+ = render("notes", card:)
35
+
36
+ = yield :right
@@ -0,0 +1,2 @@
1
+ = turbo_stream.refresh request_id: nil
2
+ = turbo_stream.remove dom_id(object, :modal)
@@ -0,0 +1,12 @@
1
+ = Example id: name do |ex|
2
+ = ex.Head do
3
+ = ex.Title ui.class(name), tag: :h2, href: "##{name}"
4
+
5
+ = ex.Body do
6
+ = yield
7
+
8
+ = ex.Source tag: :pre, class: "highlight" do
9
+ - formatter = Rouge::Formatters::HTML.new
10
+ - lexer = Rouge::Lexers::Haml.new
11
+ - source = get_partial(partial).encode!
12
+ = raw formatter.format(lexer.lex(source))
@@ -0,0 +1,16 @@
1
+ = stylesheet_link_tag 'ui/Example'
2
+
3
+ -# :javascript
4
+ -# window.onhashchange = () => {
5
+ -# $("[href]").remove(".current")
6
+ -# $("[href='" + location.hash + "']").add(".current")
7
+ -# }
8
+
9
+ :javascript
10
+ window.addEventListener('load', () => {
11
+ $
12
+ })
13
+
14
+ - @examples.each do |name, partial|
15
+ = render "cafe_car/examples/example", name:, partial: do
16
+ = render partial, href: "##{name}"
@@ -0,0 +1,2 @@
1
+ - @examples.each do |name, _|
2
+ = navigation.link_to ui.class(name), "##{name}"
@@ -0,0 +1,4 @@
1
+ = Alert :success, "Success alert. #{Faker::Lorem.paragraph}"
2
+ = Alert :info, "Info alert. #{Faker::Lorem.paragraph}"
3
+ = Alert :error, "Error alert. #{Faker::Lorem.paragraph}"
4
+ = Alert :warning, "Warning alert. #{Faker::Lorem.sentence}"
@@ -0,0 +1,3 @@
1
+ = Button "Default", href:
2
+ = Button "Primary", :primary, href:
3
+ = Button "Danger", :danger, href:
@@ -0,0 +1,6 @@
1
+ = Card title: "Card Title", actions: link_to("Action", "#Card") do |c|
2
+ = c.Section do
3
+ Card Section
4
+
5
+ = c.Foot do
6
+ Card Foot
@@ -0,0 +1,3 @@
1
+ = Chat do |c|
2
+ = c.Message :local, "Hello, world!"
3
+ = c.Message :remote, "And hello to you!"
@@ -0,0 +1,3 @@
1
+ = Controls do |c|
2
+ = c.Link "Link 1", href:
3
+ = c.Link "Link 2", href:
@@ -0,0 +1 @@
1
+ = Error "Error"
@@ -0,0 +1,9 @@
1
+ = Field do |f|
2
+ = f.Label "Label"
3
+ = f.Input tag: :input, placeholder: "placeholder"
4
+ = f.Hint "Hint"
5
+
6
+ = Field do |f|
7
+ = f.Label "Label"
8
+ = f.Error "has an error"
9
+ = f.Input tag: :input, placeholder: "with error"
@@ -0,0 +1,11 @@
1
+ = Grid columns: 3 do
2
+ = Card title: "1"
3
+ = Card title: "2"
4
+ = Card title: "3"
5
+ = Card title: "4"
6
+
7
+ = Grid style: "grid-auto-columns: minmax(min-content, 2fr)" do
8
+ = Card title: "1"
9
+ = Card title: "2"
10
+ = Card title: "3"
11
+ = Card title: "4"
@@ -0,0 +1,21 @@
1
+ = Group do
2
+ = Button(href:) { "First" }
3
+ = Button(href:) { "Second" }
4
+ = Button(href:) { "Third" }
5
+
6
+ = Group do
7
+ = Card { "First Card" }
8
+ = Card { "Second Card" }
9
+ = Card { "Third Card" }
10
+
11
+ = Field label: "Inputs" do |f|
12
+ = Group do
13
+ = f.Input tag: :input, value: "first", class: "auto"
14
+ = f.Input tag: :input, value: "second", class: "auto"
15
+ = f.Input tag: :input, value: "third", class: "auto"
16
+
17
+ = Field label: "Input" do |f|
18
+ = f.Error "ERROR"
19
+ = Group do
20
+ = Input "$", tag: :b
21
+ = Input tag: :input, value: "value", class: "auto"
@@ -0,0 +1 @@
1
+ = InfoCircle "Hi", tip: "Hello"
@@ -0,0 +1,5 @@
1
+ = Menu do |m|
2
+ = m.Item href: "#Menu" do
3
+ = icon(:copy) { "Copy" }
4
+ = m.Item icon("Item 2"), href: "#Menu"
5
+ = m.Item icon("Item 3"), href: "#Menu"
@@ -0,0 +1,4 @@
1
+ = Modal do |m|
2
+ = m.Body do
3
+ = Card title: "Card in modal", actions: m.Close { icon(:xmark_circle_solid) } do |c|
4
+ = c.Section "Body"
@@ -0,0 +1,4 @@
1
+ = Navigation do |n|
2
+ = n.Link "First", href: "#Navigation", id: "first"
3
+ = n.Link "Second", href: "#Navigation", id: "second"
4
+ = n.Link "Third", href: "#Navigation", id: "third"
@@ -0,0 +1,4 @@
1
+ = Page title: "Title", actions: "Actions" do |p|
2
+ = p.Aside "Left aside"
3
+ = p.Body "Page body"
4
+ = p.Aside "Right aside"
@@ -0,0 +1,13 @@
1
+ = Table do |t|
2
+ = t.Head do
3
+ = t.Cell "Column ↓", href: "#Table"
4
+ = t.Cell "Another"
5
+
6
+ = t.Body do
7
+ = t.Row do
8
+ = t.Cell "A cell"
9
+ = t.Cell "Another cell"
10
+
11
+ = t.Row do
12
+ = t.Cell "Another row"
13
+ = t.Cell :shy, tag.span("Shy cell")
@@ -0,0 +1,8 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %meta{http_equiv: "Content-Type", content: 'text/html; charset=utf-8'}
5
+ :css
6
+ body {}
7
+
8
+ %body= yield
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,3 @@
1
+ <div class="trix-content">
2
+ <%= yield -%>
3
+ </div>
@@ -0,0 +1,4 @@
1
+ !!!
2
+ %html{lang: I18n.locale}
3
+ %head= render "head"
4
+ %body{class: body_classes}= render "body"
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <style>
6
+ /* Email styles need to be inline */
7
+ </style>
8
+ </head>
9
+
10
+ <body>
11
+ <%= yield %>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1 @@
1
+ = f.input :body
@@ -0,0 +1,5 @@
1
+ %p
2
+ You can reset your password on
3
+ #{link_to "this password reset page", edit_password_url(@user.password_reset_token)}.
4
+
5
+ This link will expire in #{distance_of_time_in_words(0, @user.password_reset_token_expires_in)}.
@@ -0,0 +1,4 @@
1
+ You can reset your password on
2
+ <%= edit_password_url(@user.password_reset_token) %>
3
+
4
+ This link will expire in <%= distance_of_time_in_words(0, @user.password_reset_token_expires_in) %>.
@@ -0,0 +1,8 @@
1
+ = card.Image href:, src: image.url if image
2
+
3
+ = card.Head do
4
+ = card.Title(href:) { title }
5
+ = card.Subtitle { subtitle }
6
+ = card.Actions { actions }
7
+
8
+ = yield
@@ -0,0 +1 @@
1
+ = yield
@@ -0,0 +1 @@
1
+ = yield || "✕"
@@ -0,0 +1,7 @@
1
+ = page.Head do
2
+ = ui.Layout.Menu
3
+ = page.Title { page.title }
4
+ = page.Tabs { tabs }
5
+ = page.Actions { actions }
6
+
7
+ = yield
@@ -0,0 +1,77 @@
1
+ {
2
+ "ignored_warnings": [
3
+ {
4
+ "warning_type": "Dangerous Eval",
5
+ "warning_code": 13,
6
+ "fingerprint": "9f7c27406a84219ad1d93b07b8c1acd435ad75921861a85a647e32f3d07d1289",
7
+ "check_name": "Evaluation",
8
+ "message": "Dynamic string evaluated as code",
9
+ "file": "lib/cafe_car/auto_resolver.rb",
10
+ "line": 16,
11
+ "link": "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
12
+ "code": "TOPLEVEL_BINDING.eval(\"class #{mod.name}::#{name} < CafeCar[:ApplicationController]\\n include CafeCar::Controller\\n cafe_car\\n self\\nend\\n\", \"lib/cafe_car/auto_resolver.rb\", 16)",
13
+ "render_path": null,
14
+ "location": {
15
+ "type": "method",
16
+ "class": "CafeCar::AutoResolver",
17
+ "method": "define"
18
+ },
19
+ "user_input": null,
20
+ "confidence": "Weak",
21
+ "cwe_id": [
22
+ 913,
23
+ 95
24
+ ],
25
+ "note": ""
26
+ },
27
+ {
28
+ "warning_type": "Dangerous Eval",
29
+ "warning_code": 13,
30
+ "fingerprint": "48dbe3b4398088c5313b96ac86384413140275ce8852e516f8f3638b4ffdf999",
31
+ "check_name": "Evaluation",
32
+ "message": "Dynamic string evaluated as code",
33
+ "file": "lib/cafe_car/caching.rb",
34
+ "line": 15,
35
+ "link": "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
36
+ "code": "_1.module_eval(\"def #{name} = cache(:#{name}) { super }\\n\")",
37
+ "render_path": null,
38
+ "location": {
39
+ "type": "method",
40
+ "class": "CafeCar::Caching",
41
+ "method": "cache!"
42
+ },
43
+ "user_input": null,
44
+ "confidence": "Weak",
45
+ "cwe_id": [
46
+ 913,
47
+ 95
48
+ ],
49
+ "note": ""
50
+ },
51
+ {
52
+ "warning_type": "Dangerous Eval",
53
+ "warning_code": 13,
54
+ "fingerprint": "f14f192898bdd59525b84317d91ecbbdc952f71e28dbeca457caf4c11bee0bc7",
55
+ "check_name": "Evaluation",
56
+ "message": "Dynamic string evaluated as code",
57
+ "file": "lib/cafe_car/auto_resolver.rb",
58
+ "line": 24,
59
+ "link": "https://brakemanscanner.org/docs/warning_types/dangerous_eval/",
60
+ "code": "TOPLEVEL_BINDING.eval(\"class #{mod.name}::#{name} < CafeCar[:ApplicationPolicy]\\n def admin? = Rails.env.development?\\n\\n def index? = admin?\\n def show? = admin?\\n def create? = admin?\\n def update? = admin?\\n def destroy? = admin?\\n\\n def permitted_attributes\\n model.info.fields.names.then do |names|\\n [*model.primary_key].reverse.map(&:to_sym) & names | names\\n end\\n end\\n\\n class Scope < Scope\\n def resolve = scope.all\\n end\\n self\\nend\\n\", \"lib/cafe_car/auto_resolver.rb\", 24)",
61
+ "render_path": null,
62
+ "location": {
63
+ "type": "method",
64
+ "class": "CafeCar::AutoResolver",
65
+ "method": "define"
66
+ },
67
+ "user_input": null,
68
+ "confidence": "Weak",
69
+ "cwe_id": [
70
+ 913,
71
+ 95
72
+ ],
73
+ "note": ""
74
+ }
75
+ ],
76
+ "brakeman_version": "8.0.4"
77
+ }
@@ -0,0 +1,12 @@
1
+ pin "application"
2
+ pin "cafe_car"
3
+
4
+ pin "@hotwired/turbo-rails", to: "turbo.min.js"
5
+ pin "@rails/actiontext", to: "actiontext.esm.js"
6
+ pin "trix"
7
+
8
+ # pin "@hotwired/stimulus", to: "stimulus.min.js"
9
+ # pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
10
+ # pin_all_from "app/javascript/controllers", under: "controllers"
11
+ pin "trix"
12
+ pin "@rails/actiontext", to: "actiontext.esm.js"
@@ -0,0 +1,63 @@
1
+ en:
2
+ "true": "Yes"
3
+ "false": "No"
4
+ index: List
5
+ show: View
6
+ destroy: Delete
7
+ new_session: Log In
8
+ auth_required: Please %{new_session} first.
9
+
10
+ navigation:
11
+ icon:
12
+ new_session: log-in
13
+ sessions: log-in
14
+ attachments: media-image-list
15
+ blobs: media-image-folder
16
+ passwords: key
17
+ versions: multiple-pages
18
+
19
+ list_html:
20
+ zero: "(none)"
21
+ one: "%{list}"
22
+ other: "%{count} total: %{list}"
23
+
24
+ controls:
25
+ default: "%{Action}"
26
+ new: "%{Action} %{Model}"
27
+ index: "%{Models}"
28
+ confirm:
29
+ destroy: This %{Model} will be PERMANENTLY deleted. Are you sure?
30
+ disabled:
31
+ policy:
32
+ default: You don't have permission to %{action} this %{model}.
33
+ index: You don't have permission to %{action} %{models}.
34
+
35
+ time:
36
+ formats:
37
+ default: "%b %{ordinal}, %Y at %-l:%M%p"
38
+ long: "%A, %B %{ordinal}, %Y at %-l:%M:%S%p %Z"
39
+
40
+ attributes:
41
+ to_s: Item
42
+ id: ID
43
+ ip: IP
44
+ ip_address: IP address
45
+ created_at: Created
46
+ updated_at: Updated
47
+ deleted_at: Deleted
48
+ published_at: Published
49
+
50
+ flashes:
51
+ create_html: "%{Model} created!"
52
+ update_html: "%{Model} updated!"
53
+ destroy_html: "%{Model} deleted!"
54
+
55
+ activerecord:
56
+ models:
57
+ application_record: Record # .model_name.human
58
+ person:
59
+ one: Person
60
+ other: People
61
+ attributes:
62
+ article:
63
+ title: Title # .human_attribute_name("title")
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ CafeCar::Engine.routes.draw do
2
+ scope module: :cafe_car, as: :cafe_car do
3
+ get "components", to: "examples#index"
4
+ end
5
+
6
+ # Opt-in login/logout. Singular resource so the form posts to /session and
7
+ # request_authentication can redirect to new_session_path.
8
+ resource :session, only: %i[new create destroy], controller: "cafe_car/sessions"
9
+ end
@@ -0,0 +1,13 @@
1
+ class CreateSlugs < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :slugs do |t|
4
+ t.string :slug, null: false
5
+ t.references :sluggable, polymorphic: true
6
+ t.string :scope
7
+ t.datetime :created_at
8
+ end
9
+
10
+ add_index :slugs, [ :slug, :sluggable_type ]
11
+ add_index :slugs, [ :slug, :sluggable_type, :scope ], unique: true
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module CafeCar::ActiveRecord
2
+ module SQLite3Extension
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ alias_method :pre_cafe_car_initialize, :initialize
7
+ private :pre_cafe_car_initialize
8
+
9
+ def initialize(*args)
10
+ pre_cafe_car_initialize(*args)
11
+
12
+ raw_connection.create_function("regexp", -1) do |func, pattern, expression, case_sensitive|
13
+ options = 0
14
+ options |= Regexp::IGNORECASE if case_sensitive.zero?
15
+ pattern = Regexp.new(pattern.to_s, options)
16
+ func.result = expression.to_s.match(pattern) ? 1 : 0
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require "responders"
2
+
3
+ module CafeCar
4
+ class ApplicationResponder < ActionController::Responder
5
+ include Responders::FlashResponder
6
+ include Responders::HttpCacheResponder
7
+
8
+ self.error_status = :unprocessable_entity
9
+ self.redirect_status = :see_other
10
+
11
+ def to_turbo_stream
12
+ # Put :html back in the accepted format list. respond_with removes it
13
+ controller.lookup_context.formats << :html
14
+ to_html
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ class CafeCar::Attributes
2
+ attr_reader :user, :object, :permitted
3
+
4
+ def initialize(user, object, permitted_attributes)
5
+ @user = user
6
+ @object = object
7
+ @permitted = [ *permitted_attributes ]
8
+ process_attributes!
9
+ end
10
+
11
+ def info(method) = CafeCar[:FieldInfo].new(object:, method:)
12
+
13
+ def editable
14
+ @editable ||= @permitted.map()
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def process_attributes!
21
+ @editable = @permitted.clone
22
+ end
23
+ end
@@ -0,0 +1,49 @@
1
+ module CafeCar
2
+ module AutoResolver
3
+ def auto_resolve!(mod)
4
+ mod.instance_eval <<~RUBY, __FILE__, __LINE__
5
+ def const_missing(name)
6
+ super
7
+ rescue NameError
8
+ CafeCar.define(self, name) or raise
9
+ end
10
+ RUBY
11
+ end
12
+
13
+ def define(mod, name)
14
+ case name.to_s
15
+ when /^\w+Controller$/
16
+ TOPLEVEL_BINDING.eval <<~RUBY, __FILE__, __LINE__
17
+ class #{mod.name}::#{name} < CafeCar[:ApplicationController]
18
+ include CafeCar::Controller
19
+ cafe_car
20
+ self
21
+ end
22
+ RUBY
23
+ when /^\w+Policy$/
24
+ TOPLEVEL_BINDING.eval <<~RUBY, __FILE__, __LINE__
25
+ class #{mod.name}::#{name} < CafeCar[:ApplicationPolicy]
26
+ def admin? = Rails.env.development?
27
+
28
+ def index? = admin?
29
+ def show? = admin?
30
+ def create? = admin?
31
+ def update? = admin?
32
+ def destroy? = admin?
33
+
34
+ def permitted_attributes
35
+ model.info.fields.names.then do |names|
36
+ [*model.primary_key].reverse.map(&:to_sym) & names | names
37
+ end
38
+ end
39
+
40
+ class Scope < Scope
41
+ def resolve = scope.all
42
+ end
43
+ self
44
+ end
45
+ RUBY
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ module CafeCar::Caching
2
+ extend ActiveSupport::Concern
3
+
4
+ def cache(name, &)
5
+ instance_variable_get("@#{name}") || instance_variable_set("@#{name}", instance_exec(&))
6
+ end
7
+
8
+ class_methods do
9
+ def derive(name, proc = nil, &block)
10
+ proc ||= block
11
+ define_method(name) { cache(name, &proc) }
12
+ end
13
+
14
+ def cache!(name)
15
+ prepend Module.new.tap { _1.module_eval <<~RUBY }
16
+ def #{name} = cache(:#{name}) { super }
17
+ RUBY
18
+ end
19
+ end
20
+ end