potassium 6.5.0 → 6.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +30 -1
  4. data/docs/CONTRIBUTING.md +2 -2
  5. data/lib/potassium/assets/.circleci/config.yml.erb +11 -1
  6. data/lib/potassium/assets/.eslintrc.json +15 -9
  7. data/lib/potassium/assets/.rubocop.yml +1 -0
  8. data/lib/potassium/assets/Aptfile +5 -0
  9. data/lib/potassium/assets/README.yml +30 -6
  10. data/lib/potassium/assets/active_admin/admin-component.vue +22 -30
  11. data/lib/potassium/assets/app/javascript/{app.spec.js → components/app.spec.ts} +1 -1
  12. data/lib/potassium/assets/app/javascript/components/app.vue +9 -0
  13. data/lib/potassium/assets/app/javascript/types/vue.d.ts +5 -0
  14. data/lib/potassium/assets/app/jobs/shrine_promote_job.rb +14 -0
  15. data/lib/potassium/assets/app/mailers/application_mailer.rb +1 -1
  16. data/lib/potassium/assets/app/mailers/example_mailer.rb +6 -0
  17. data/lib/potassium/assets/app/serializers/base_serializer.rb +3 -0
  18. data/lib/potassium/assets/app/serializers/concerns/image_handling_attributes.rb +20 -0
  19. data/lib/potassium/assets/app/uploaders/cover_image_uploader.rb +52 -0
  20. data/lib/potassium/assets/app/views/example_mailer/example_mail.html.mjml +7 -0
  21. data/lib/potassium/assets/app/views/layouts/default_mail.html.mjml +49 -0
  22. data/lib/potassium/assets/config/initializers/shrine/plugins/image_handling_utilities.rb +143 -0
  23. data/lib/potassium/assets/config/mailer.rb.erb +0 -2
  24. data/lib/potassium/assets/config/shrine.rb +15 -0
  25. data/lib/potassium/assets/config/webpack/rules/css.js +5 -0
  26. data/lib/potassium/assets/config/webpack/rules/index.js +11 -0
  27. data/lib/potassium/assets/config/webpack/rules/jquery.js +11 -0
  28. data/lib/potassium/assets/config/webpack/rules/typescript.js +32 -0
  29. data/lib/potassium/assets/config/webpack/rules/vue.js +19 -0
  30. data/lib/potassium/assets/config/webpack/webpack.config.js +4 -0
  31. data/lib/potassium/assets/public/mails/platanus-logo.png +0 -0
  32. data/lib/potassium/assets/tsconfig.json +31 -0
  33. data/lib/potassium/cli/commands/create.rb +3 -1
  34. data/lib/potassium/cli_options.rb +15 -3
  35. data/lib/potassium/platanus_config.rb +20 -0
  36. data/lib/potassium/recipes/admin.rb +11 -0
  37. data/lib/potassium/recipes/api.rb +6 -85
  38. data/lib/potassium/recipes/coverage.rb +31 -0
  39. data/lib/potassium/recipes/file_storage.rb +50 -0
  40. data/lib/potassium/recipes/front_end.rb +82 -110
  41. data/lib/potassium/recipes/google_tag_manager.rb +1 -1
  42. data/lib/potassium/recipes/mailer.rb +22 -10
  43. data/lib/potassium/recipes/mjml.rb +31 -0
  44. data/lib/potassium/recipes/node.rb +11 -13
  45. data/lib/potassium/recipes/style.rb +9 -2
  46. data/lib/potassium/recipes/vue_admin.rb +38 -8
  47. data/lib/potassium/templates/application.rb +1 -0
  48. data/lib/potassium/version.rb +8 -3
  49. data/spec/features/api_spec.rb +6 -1
  50. data/spec/features/coverage_spec.rb +17 -0
  51. data/spec/features/file_storage_spec.rb +102 -26
  52. data/spec/features/front_end_spec.rb +14 -48
  53. data/spec/features/mailer_spec.rb +79 -33
  54. data/spec/features/mjml_spec.rb +53 -0
  55. data/spec/features/vue_admin_spec.rb +0 -10
  56. data/spec/support/shared_examples.rb +5 -0
  57. metadata +28 -21
  58. data/lib/potassium/assets/active_admin/admin_application.js +0 -14
  59. data/lib/potassium/assets/active_admin/init_activeadmin_vue.rb +0 -10
  60. data/lib/potassium/assets/app/graphql/graphql_controller.rb +0 -55
  61. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +0 -23
  62. data/lib/potassium/assets/app/graphql/queries/base_query.rb +0 -4
  63. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +0 -4
  64. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +0 -4
  65. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +0 -5
  66. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +0 -5
  67. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +0 -7
  68. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +0 -5
  69. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +0 -4
  70. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +0 -4
  71. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +0 -10
  72. data/lib/potassium/assets/app/graphql/types/query_type.rb +0 -13
  73. data/lib/potassium/assets/config/graphql_playground.rb +0 -20
  74. data/spec/features/graphql_spec.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf3d3969e70d29a2f2c15992c48f609ad389a486560fd6a16ea3a74871328a19
4
- data.tar.gz: c1617bc3cba761408d2cba7419fbe4c68cb265a7438ea07b8c9b637944e3271d
3
+ metadata.gz: 0bc57ea8c0f92d1ce3dd15feef9164f7bef381ce322051bd810a3303c8ca257c
4
+ data.tar.gz: f31b33309764331c194d97380a37ccd0afdfa50ccd38eeba7931e330262179c3
5
5
  SHA512:
6
- metadata.gz: 61cbdbbced51469867a32a83a97628f8ea4ccfb3290cc2b2e6e8a34f86e53a6e0478609326264b59324266b8db184cd449d921a9f17cc3f5a58be01eddb00e3b
7
- data.tar.gz: '00900211ab3d5dcb75e4f45889778c752cda9f61a7e8d8cd0e264bb1ba678c50f8b9593f8bb98c3afd8b2fa554458cb639bfba202428a978976872cf45d291a5'
6
+ metadata.gz: c88f8cd6cb7f6814233c608c05aabb77369ab11887a64256efbba4e813c8cf1d2ee9c4fffc3ca9c45e24618b70e8e652318969cd38f50d7741888e06bdde1333
7
+ data.tar.gz: 788dbd755914cae98ca2bbd6c4f3566deacdf8eccba0a0b50eaff92facbc95b31bd23ec31a9a4026e0643dfe9fa0a8a888b3c962dd3cc557c184b3d69f245a54
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 6.6.0
6
+ Features
7
+ - Update power api gem to use v2.0.0. Install "internal" API mode [#394](https://github.com/platanus/potassium/pull/394)
8
+ - Updates Webpacker to Shakapacker, upgrading Vue and TailwindCSS to their latest versions [#395](https://github.com/platanus/potassium/pull/395)
9
+ - Add some image handling and processing in shrine file storage option [#398](https://github.com/platanus/potassium/pull/398)
10
+ - Include `--platanus-config` option to skip most of the instalation options [#399](https://github.com/platanus/potassium/pull/399).
11
+ - Add [`rubocop-platanus`](https://github.com/platanus/rubocop-platanus) gem for linting platanus' best practices [#402](https://github.com/platanus/potassium/pull/402).
12
+ - Remove graphql option [#404](https://github.com/platanus/potassium/pull/404)
13
+ - Add frontend testing coverage for Jest [#401](https://github.com/platanus/potassium/pull/401) and associated reviewdog comments [#406](https://github.com/platanus/potassium/pull/406)
14
+ - Add MJML to handle mail templates, with example [#405](https://github.com/platanus/potassium/pull/405)
15
+
16
+ Fixes
17
+ - Add missing vips CI config when selecting shrine [#403](https://github.com/platanus/potassium/pull/403).
18
+ - Fix shakapacker to version 6.2.x to avoid [this bug](https://github.com/shakacode/shakapacker/issues/123) [#404](https://github.com/platanus/potassium/pull/404)
19
+
3
20
  ## 6.5.0
4
21
 
5
22
  Features
@@ -9,9 +26,12 @@ Features
9
26
  - Add SimpleCov recipe [#387](https://github.com/platanus/potassium/pull/387)
10
27
  - Update Rails to 6.1 [#389](https://github.com/platanus/potassium/pull/389) & [#392](https://github.com/platanus/potassium/pull/392)
11
28
  - Include `run_test` as a valid example group [#379](https://github.com/platanus/potassium/pull/379). This was added incorrectly in this [PR](https://github.com/platanus/potassium/pull/379).
29
+ - Add system tests configuration [#388](https://github.com/platanus/potassium/pull/388)
30
+ - Allow CircleCI cache clearing [#383](https://github.com/platanus/potassium/pull/383)
12
31
 
13
32
  Fixes
14
33
  - Remove rails_stdout_logging gem because it is no longer needed after Rails 5 and it was generating a deprecation warning [#325](https://github.com/platanus/potassium/pull/325)
34
+ - Fix default action dissapearance when using `binding.pry` in generated projects [#385](https://github.com/platanus/potassium/pull/385)
15
35
 
16
36
  ## 6.4.0
17
37
 
data/README.md CHANGED
@@ -21,6 +21,34 @@ Use the `potassium create` command to create a new project:
21
21
  > 2. If you feel that it's too slow, you may need to update rubygems: `gem update --system`.
22
22
  > 3. Potassium uses node under the hood, so a check will also be performed to ensure you are running the supported version.
23
23
 
24
+ #### Platanus Configutarion
25
+
26
+ In case you want to use the Platanus Configuration you should use the following command:
27
+
28
+ $ potassium create <project-name> --platanus-config
29
+
30
+ This will create a project with the following configuration:
31
+ 1. `database`: `'postgresql'`
32
+ 2. `local`: `'es-CL'`
33
+ 3. `email_service`: `'sendgrid'`
34
+ 4. `devise`: `true`
35
+ 5. `devise-user-model`: `true`
36
+ 6. `admin`: `true`
37
+ 7. `vue_admin`: `true`
38
+ 8. `pundit`: `true`
39
+ 9. `api`: `true`
40
+ 10. `storage`: `'shrine'`
41
+ 11. `heroku`: `true`
42
+ 12. `background_processor`: `true`
43
+ 13. `draper`: `true`
44
+ 14. `schedule`: `true`
45
+ 15. `sentry`: `true`
46
+ 16. `front_end`: `'vue'`
47
+ 17. `google_tag_manager`: `true`
48
+ 18. `test`: `true`
49
+ 19. `spring`: `true`
50
+
51
+ The remaining question will be asked as usual.
24
52
  ### Adding recipes to an existing project
25
53
 
26
54
  Use the `potassium install` command to add a recipe to a project:
@@ -53,6 +81,7 @@ Potassium Rails apps includes the following gems and technologies:
53
81
  - [Tzinfo-Data](https://github.com/tzinfo/tzinfo-data) for updating timezone information
54
82
  - [Faker](https://github.com/stympy/faker) for creating development data
55
83
  - [Scout](https://github.com/scoutapp/scout_apm_ruby) for monitoring performance
84
+ - [Mjml](https://github.com/sighmon/mjml-rails) for mails style
56
85
 
57
86
  The following optional integrations are also added:
58
87
 
@@ -177,4 +206,4 @@ potassium is maintained by [platanus](http://platan.us).
177
206
 
178
207
  ## License
179
208
 
180
- Potassium is © 2014 Platanus, SPA. It is free software and may be redistributed under the terms specified in the LICENSE file.
209
+ Potassium is © 2014 Platanus, SPA. It is free software and may be redistributed under the terms specified in the [LICENSE](/LICENSE.txt) file.
data/docs/CONTRIBUTING.md CHANGED
@@ -54,7 +54,7 @@ This method is used if you need to ask something to the user before doing someth
54
54
  ```ruby
55
55
  def ask
56
56
  use_banana_split = answer(:banana_split) do
57
- Ask.confirm("Do you wan to use Banana Split?")
57
+ Ask.confirm("Do you want to use Banana Split?")
58
58
  end
59
59
  set(:use_banana_split, true) if use_banana_split
60
60
  end
@@ -103,7 +103,7 @@ For example if you run `portassium install devise` this will use
103
103
  [the recipe template](/lib/potassium/templates/recipe.rb) to load an execute the
104
104
  `install` method for the **devise** recipe.
105
105
 
106
- You can defined the main functionallity of a recipe in a private method and call
106
+ You can define the main functionallity of a recipe in a private method and call
107
107
  it from the `create` and `install` methods.
108
108
 
109
109
  ```ruby
@@ -78,6 +78,13 @@ commands:
78
78
  key: yarn-dependencies-{{ .Environment.YARN_CACHE_VERSION }}-{{ checksum "yarn.lock" }}
79
79
  paths:
80
80
  - node_modules
81
+ <%- if selected?(:storage, :shrine) -%>
82
+ - run:
83
+ name: Install apt and vips buildpack dependencies
84
+ command: |
85
+ xargs -a Aptfile sudo apt-get install
86
+ sudo apt-get install libvips
87
+ <%- end -%>
81
88
 
82
89
  jobs:
83
90
  test:
@@ -125,7 +132,10 @@ jobs:
125
132
  <%- if selected?(:front_end, :vue) -%>
126
133
  - run:
127
134
  name: Run jest
128
- command: yarn run test
135
+ command: |
136
+ yarn run test > coverage/input_jest.txt
137
+ ./node_modules/.bin/format-coverage coverage/input_jest.txt coverage/output_jest.txt /home/circleci/project/app/javascript
138
+ cat coverage/output_jest.txt | ./bin/reviewdog -reporter=github-pr-review -efm="%f:%l:%c: %m"
129
139
  <%- end -%>
130
140
 
131
141
  - store_test_results:
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "env": {
3
- "es6": true
3
+ "browser": true,
4
+ "es2021": true,
5
+ "node": true,
6
+ "jest/globals": true,
7
+ "vue/setup-compiler-macros": true
4
8
  },
5
9
  "parserOptions": {
6
- "ecmaVersion": 2018,
10
+ "ecmaVersion": 2020,
7
11
  "sourceType": "module"
8
12
  },
9
- "plugins": ["import"],
13
+ "plugins": ["import", "jest", "tailwindcss"],
10
14
  "settings": {
11
15
  "import/resolver": {
12
16
  "node": {
@@ -15,7 +19,10 @@
15
19
  }
16
20
  },
17
21
  "extends": [
18
- "plugin:vue/strongly-recommended"
22
+ "plugin:vue/vue3-recommended",
23
+ "@vue/typescript/recommended",
24
+ "@vue/eslint-config-typescript",
25
+ "plugin:tailwindcss/recommended"
19
26
  ],
20
27
  "rules": {
21
28
  "accessor-pairs": 0,
@@ -338,14 +345,13 @@
338
345
  "vue/max-len": ["error", {
339
346
  "code": 120,
340
347
  "ignoreHTMLAttributeValues": true
341
- }]
348
+ }]
342
349
  },
343
350
  "overrides": [
344
351
  {
345
- "files": ["**/*.js"],
346
- "excludedFiles": "app/**/*.js",
347
- "env": {
348
- "node": true
352
+ "files": ["*.ts", "*.vue"],
353
+ "rules": {
354
+ "no-undef": "off"
349
355
  }
350
356
  }
351
357
  ]
@@ -2,6 +2,7 @@ require:
2
2
  - rubocop-rspec
3
3
  - rubocop-rails
4
4
  - rubocop-performance
5
+ - rubocop-platanus
5
6
  AllCops:
6
7
  Exclude:
7
8
  - "vendor/**/*"
@@ -0,0 +1,5 @@
1
+ libglib2.0-0
2
+ libglib2.0-dev
3
+ libpoppler-glib8
4
+ libwebp-dev
5
+ webp
@@ -27,7 +27,7 @@ readme:
27
27
  body: |
28
28
  For hot-reloading and fast webpacker compilation you need to run webpack's dev server along with the rails server:
29
29
 
30
- $ ./bin/webpack-dev-server
30
+ $ ./bin/webpacker-dev-server
31
31
 
32
32
  Running the dev server will also solve problems with the cache not refreshing between changes and provide better error messages if something fails to compile.
33
33
 
@@ -105,7 +105,34 @@ readme:
105
105
  body: "For managing uploads, this project uses [Active Storage](https://github.com/rails/rails/tree/master/activestorage)."
106
106
  shrine:
107
107
  title: "File Storage"
108
- body: "For managing uploads, this project uses [Shrine](https://github.com/shrinerb/shrine)."
108
+ body: |
109
+ For managing uploads, this project uses [Shrine](https://github.com/shrinerb/shrine). When generated, this project includes the following files and configurations:
110
+
111
+ - `ImageUploader` that includes file type validation
112
+ - `CoverImageUploader`, inheriting from `ImageUploader`. It does a couple of things:
113
+ - Generates derivatives in `jpg` and `webp` format, for three different sizes. For an attachment of name `image`, to get the url for a derivative, let's say `sm`, you would do `record.image_url(:sm)`
114
+ - Saves a [blurhash](https://blurha.sh/) code to the attachment metadata
115
+ - `ImageHandlingUtilities`, a shrine plugin in the initializers folder that is used in the `CoverImageUploader`. Given a model with an attachment of name `image`, it adds the following methods to the model:
116
+ - `image_blurhash`: returns blurhash from metadata
117
+ - `generate_image_derivatives`: It generates all derivatives defined in Uploader. If file already had derivatives, it replaces them with newly generated ones. Associated class method: `generate_all_image_derivatives`
118
+ - `generate_image_metadata`: refreshes all metadata for attachment. Associated class method: `generate_all_image_metadata`
119
+ - `generate_image_derivatives_and_metadata`: does both previous things. Useful because it does so opening the file only once. Associated class method: `generate_all_image_derivatives_and_metadata`
120
+
121
+ Class methods are the same as their instance counterparts, but for collections. They also allow error handling on an individual record by passing a block to handle each error. If no block is given, attachments that throw errors are ignored and the iteration continues
122
+ - `ImageHandlingAttributes` serializer concern. It adds a method `add_image_handling_attributes` to all serializers that inherit from `BaseSerializer`. Considering an attachment of name `image`, this method adds two attributes to the serialized record:
123
+ - `image_blurhash`
124
+ - `image`. This is a hash that includes urls for all derivatives passed to the method. For example:
125
+ ```
126
+ add_image_handling_attributes(attachment_name: :image, derivatives: [:sm, :md], include_original_image: true)
127
+
128
+ # results in the following hash for the image attribute:
129
+ # {
130
+ # sm: { url: 'someurl.com/bla' },
131
+ # md: { url: 'someurl.com/ble' },
132
+ # original: { url: 'someurl.com/ble' }
133
+ # }
134
+ ```
135
+ - `SHRINE_SECRET_KEY` environment variable. It comes witha value set in `.env.development`, but you'll need to set one for it in staging and production. It can be any random value, generating it with `SecureRandom.hex` for instance
109
136
  pundit:
110
137
  title: "Authorization"
111
138
  body: "For defining which parts of the system each user has access to, we have chosen to include the [Pundit](https://github.com/elabs/pundit) gem, by [Elabs](http://elabs.se/)."
@@ -127,16 +154,13 @@ readme:
127
154
  power_api:
128
155
  title: "API Support"
129
156
  body: "This projects uses [Power API](https://github.com/platanus/power_api). It's a Rails engine that gathers a set of gems and configurations designed to build incredible REST APIs."
130
- graphql:
131
- title: "API Support"
132
- body: "This projects uses [graphql-ruby](https://graphql-ruby.org/) to generate a GraphQL API."
133
157
  active_admin:
134
158
  title: "Administration"
135
159
  body: |
136
160
  This project uses [Active Admin](https://github.com/activeadmin/activeadmin) which is a Ruby on Rails framework for creating elegant backends for website administration.
137
161
  <% if get(:vue_admin) %>
138
162
  This project supports Vue inside ActiveAdmin
139
- - The main package is located in `app/javascript/packs/admin_application.js`, here you will declare the components you want to include in your ActiveAdmin views as you would in a normal Vue App.
163
+ - The main package is located in `app/javascript/active_admin.js`, here you will declare the components you want to include in your ActiveAdmin views as you would in a normal Vue App.
140
164
  - Additionally, to be able to use Vue components as [Arbre](https://github.com/activeadmin/arbre) Nodes the component names are also declared in `config/initializers/active_admin.rb`
141
165
  - The generator includes an example component called `admin_component`, you can use this component inside any ActiveAdmin view by just writing `admin_component` as you would with any `html` tag.
142
166
  - For example:
@@ -1,35 +1,27 @@
1
+ <script setup lang="ts">
2
+ interface Props {
3
+ test?: string
4
+ testNumber?: number
5
+ testObject?: {[key: string]: string},
6
+ testList?: number[],
7
+ }
8
+
9
+ const props = withDefaults(
10
+ defineProps<Props>(),
11
+ {
12
+ test: undefined,
13
+ testNumber: 0,
14
+ testObject: undefined,
15
+ testList: undefined,
16
+ },
17
+ );
18
+
19
+ const message = 'Hello World';
20
+ </script>
21
+
1
22
  <template>
2
23
  <div>
3
- I am a Vue Component {{ test }} {{ message }}
24
+ I am a Vue Component {{ props.test }} {{ message }}
4
25
  <slot />
5
26
  </div>
6
27
  </template>
7
-
8
- <script>
9
- export default {
10
- props: {
11
- test: {
12
- type: String,
13
- default: '',
14
- },
15
- testNumber: {
16
- type: Number,
17
- default: 0,
18
- },
19
- testObject: {
20
- type: Object,
21
- default: null,
22
- },
23
- testList: {
24
- type: Array,
25
- default: null,
26
- },
27
- },
28
-
29
- data() {
30
- return {
31
- message: 'Hello World',
32
- };
33
- },
34
- };
35
- </script>
@@ -1,5 +1,5 @@
1
1
  import { shallowMount } from '@vue/test-utils';
2
- import App from 'app';
2
+ import App from './app.vue';
3
3
 
4
4
  describe('App', () => {
5
5
  test('is a Vue instance', () => {
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ const message = 'Hello Vue!';
3
+ </script>
4
+
5
+ <template>
6
+ <div id="app">
7
+ <p class="text-center text-lg">{{ message }}</p>
8
+ </div>
9
+ </template>
@@ -0,0 +1,5 @@
1
+ declare module '*.vue' {
2
+ import type { DefineComponent } from 'vue'
3
+ const component: DefineComponent<{}, {}, any>
4
+ export default component
5
+ }
@@ -0,0 +1,14 @@
1
+ class ShrinePromoteJob < ApplicationJob
2
+ queue_as :default
3
+
4
+ def perform(attacher_class, record_class, record_id, name, file_data)
5
+ attacher_class = Object.const_get(attacher_class)
6
+ record = Object.const_get(record_class).find(record_id)
7
+
8
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
9
+ attacher.create_derivatives
10
+ attacher.atomic_promote
11
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
12
+ # attachment has changed or the record has been deleted, nothing to do
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  class ApplicationMailer < ActionMailer::Base
2
- layout 'mailer'
2
+ layout 'default_mail'
3
3
  end
@@ -0,0 +1,6 @@
1
+ class ExampleMailer < ApplicationMailer
2
+ def example_mail
3
+ @email = params[:email]
4
+ mail(from: 'admin@example.com', to: @email, subject: 'Welcome to Potassium')
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class BaseSerializer < ActiveModel::Serializer
2
+ include ImageHandlingAttributes
3
+ end
@@ -0,0 +1,20 @@
1
+ module ImageHandlingAttributes
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ def add_image_handling_attributes(attachment_name:, derivatives:, include_original_image: false)
6
+ attributes attachment_name, "#{attachment_name}_blurhash".to_sym
7
+
8
+ define_method(attachment_name) do
9
+ attachment_hash = derivatives.reduce({}) do |hash, derivative|
10
+ hash[derivative] = { url: object.send("#{attachment_name}_url", derivative) }
11
+ hash
12
+ end
13
+ if include_original_image
14
+ attachment_hash[:original] = { url: object.send("#{attachment_name}_url") }
15
+ end
16
+ attachment_hash
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ require "image_processing/vips"
2
+
3
+ class CoverImageUploader < ImageUploader
4
+ plugin :derivation_endpoint, prefix: "derivations/cover_image"
5
+ plugin :add_metadata
6
+ plugin :image_handling_utilities
7
+
8
+ DERIVATIVES = {
9
+ sm: { size: [426, 240], type: 'jpg' },
10
+ md: { size: [960, 540], type: 'jpg' },
11
+ lg: { size: [1280, 720], type: 'jpg' },
12
+ webp_sm: { size: [426, 240], type: 'webp' },
13
+ webp_md: { size: [960, 540], type: 'webp' },
14
+ webp_lg: { size: [1280, 720], type: 'webp' }
15
+ }
16
+
17
+ Attacher.derivatives do |original|
18
+ vips = ImageProcessing::Vips.source(original)
19
+
20
+ DERIVATIVES.reduce({}) do |derivatives_hash, (name, derivative_info)|
21
+ derivatives_hash[name] = vips.convert(derivative_info[:type]).resize_to_limit!(
22
+ *derivative_info[:size]
23
+ )
24
+ derivatives_hash
25
+ end
26
+ end
27
+
28
+ derivation :thumbnail do |file, width, height|
29
+ ImageProcessing::Vips
30
+ .source(file)
31
+ .resize_to_limit!(width.to_i, height.to_i)
32
+ end
33
+
34
+ Attacher.default_url do |derivative: nil, **|
35
+ file&.derivation_url(:thumbnail, *DERIVATIVES.dig(derivative, :size)) if derivative.present?
36
+ end
37
+
38
+ add_metadata :blurhash do |io, derivative: nil, **|
39
+ if derivative.nil?
40
+ Shrine.with_file(io) do |file|
41
+ image = Vips::Image.new_from_file(file.path, access: :sequential)
42
+ image = image.resize(100.0 / image.width)
43
+ flat_rgb_pixels = []
44
+ image.to_a.each do |row|
45
+ row.each { |pixel| flat_rgb_pixels.concat(pixel[0..2]) }
46
+ end
47
+
48
+ Blurhash.encode(image.width, image.height, flat_rgb_pixels)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ <mj-section>
2
+ <mj-column>
3
+ <mj-text>
4
+ Hello <%= @email %>, welcome to Potassium
5
+ </mj-text>
6
+ </mj-column>
7
+ </mj-section>
@@ -0,0 +1,49 @@
1
+ <mjml>
2
+ <mj-head>
3
+ <mj-attributes>
4
+ <mj-all font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"></mj-all>
5
+ <mj-text font-weight="400" font-size="16px" color="#000000" line-height="24px" font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"></mj-text>
6
+ </mj-attributes>
7
+ <mj-style inline="inline">
8
+ .body-section {
9
+ -webkit-box-shadow: 1px 4px 11px 0px rgba(0, 0, 0, 0.15);
10
+ -moz-box-shadow: 1px 4px 11px 0px rgba(0, 0, 0, 0.15);
11
+ box-shadow: 1px 4px 11px 0px rgba(0, 0, 0, 0.15);
12
+ }
13
+ </mj-style>
14
+ <mj-style inline="inline">
15
+ .text-link {
16
+ color: #5e6ebf
17
+ }
18
+ </mj-style>
19
+ <mj-style inline="inline">
20
+ .footer-link {
21
+ color: #888888
22
+ }
23
+ </mj-style>
24
+
25
+ </mj-head>
26
+ <mj-body width="600px">
27
+ <mj-section full-width="full-width" background-color="#71717A" padding-bottom="0">
28
+ <mj-column width="100%" background-color="#A1A1AA">
29
+ <mj-spacer height="25px" />
30
+ </mj-column>
31
+ </mj-section>
32
+ <mj-section background-color="#A1A1AA" padding-bottom="0" padding-top="0">
33
+ <mj-column>
34
+ <mj-image width="100px" src="<%= image_url('/mails/platanus-logo.png') %>" align="center">
35
+ </mj-column>
36
+ <mj-column width="100%">
37
+ <mj-spacer height="25px" />
38
+ </mj-column>
39
+ </mj-section>
40
+ <mj-wrapper padding-top="0" padding-bottom="0" css-class="body-section" background-color="#ffffff" padding-left="15px" padding-right="15px">
41
+ <%= yield %>
42
+ </mj-wrapper>
43
+ <mj-section background-color="#A1A1AA" padding-bottom="0" padding-top="0">
44
+ <mj-column width="100%">
45
+ <mj-spacer height="25px" />
46
+ </mj-column>
47
+ </mj-section>
48
+ </mj-body>
49
+ </mjml>