dato-rails 0.9.0 → 0.10.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: efcf217eb4b7350ff1b1a431add8314ce1353c01c9ce2d5ffef606a0d91aa3e9
4
- data.tar.gz: 1006e161f158eff58e2449f43ec8392bd1a6812efe07dfe75b6596af1774f932
3
+ metadata.gz: 1973ec82f04620deeaf8b94c8b8fe4816c60f5c00cf0ccfba93ab38e938af5fb
4
+ data.tar.gz: 1f83767d11f764f199b4bf394da6d216a237d22a37fee001542413f5f183ab61
5
5
  SHA512:
6
- metadata.gz: 40bf39ed3afb39b32d4f3516c4bd795649b4f79eab307b9d9699e262dd91fef7ffc35ced8cf75a2bcf8c2312766d7ef63a00fe8d58235b1c6f942c441a5e4961
7
- data.tar.gz: 574dcb125c6539385851c4f0db6aefae191b8d03f7d5695394c4c92f0a7f6a0d86b581e909474c35a7bb4ba5952a0f86f0fe1103811568fffe65eca0d1705511
6
+ metadata.gz: 69b37b143b7e6f422c28f3b74677991d876a5bd181e3ebf97e9bc93d31165404bc39c32991cdbf3cd635c640fde730cc564a00c002ff4cf725870ec2703282c4
7
+ data.tar.gz: b9f95b4994a61b6801a781ba3369e679a1dbfc012d356a213de48b30bbab8a54ca2282d33a7754368c3b12b594b8b22ec265dc89b4776c083c0086707cf97dea
data/README.md CHANGED
@@ -5,7 +5,8 @@ Use [DatoCMS](https://www.datocms.com/) in your Rails application.
5
5
  This gem allows you to fetch data using Dato GraphQL APIs and render the content in your Rails app.
6
6
 
7
7
  👉 See [this simple tutorial to get started on dev.to](https://dev.to/coorasse/datocms-with-ruby-on-rails-3ae5). 👈
8
- 👉 See also [this second tutorial to build a Blog](https://dev.to/coorasse/rails-blog-with-datocms-264j-temp-slug-4336490). 👈
8
+
9
+ 👉 See also [this second tutorial to build a Blog](https://dev.to/coorasse/rails-blog-with-datocms-2h1e). 👈
9
10
 
10
11
  ## Installation
11
12
 
@@ -89,24 +90,7 @@ If you have a responsive image, you can render it with:
89
90
  render Dato::ResponsiveImage.new(node.image.responsiveImage)
90
91
  ```
91
92
 
92
- To define a custom node, you can create a new `Dato::CustomNode` view component in your application and it will be
93
- automatically used.
94
-
95
- You can also customize how each node type is rendered by specifying the mapping on the single render:
96
-
97
- ```ruby
98
- render Dato::StructuredText.new(response.data.homepage.content, overrides: { link: Dato::NotRendered })
99
- ```
100
-
101
- or globally:
102
-
103
- ```
104
- # config/initializers/dato.rb
105
-
106
- Dato::Config.overrides = {
107
- link: 'Dato::NotRendered'
108
- }.with_indifferent_access
109
- ```
93
+ Read more about [StructuredText - Custom Nodes](docs/custom_nodes.md).
110
94
 
111
95
  ## Preview and live
112
96
 
@@ -243,6 +227,7 @@ Dato::Config.configure do |config|
243
227
  config.api_token = ENV['DATO_PUBLISH_KEY'] # default: ENV.fetch("DATO_API_TOKEN", Rails.application.credentials.dig(:dato, :api_token))
244
228
  config.publish_key = ENV['DATO_PUBLISH_KEY'] # default: ENV.fetch("DATO_PUBLISH_KEY", Rails.application.credentials.dig(:dato, :publish_key))
245
229
  config.build_trigger_id = ENV['DATO_BUILD_TRIGGER_ID'] # default: ENV.fetch("DATO_BUILD_TRIGGER_ID", Rails.application.credentials.dig(:dato, :build_trigger_id))
230
+ config.base_editing_url = ENV['DATO_BASE_EDITING_URL'] # default: ENV.fetch("DATO_BASE_EDITING_URL", Rails.application.credentials.dig(:dato, :base_editing_url))
246
231
  config.mount_path = '/dato' # default: '/dato'
247
232
  end
248
233
  ```
@@ -285,18 +270,20 @@ executing `Dato::Cache.clear!` or running `bin/rails dato:cache:clear`.
285
270
 
286
271
  You can take advantage of the publish mechanism of Dato CMS to expire the cache.
287
272
 
288
- * Set the `DATO_PUBLISH_KEY` environment variable or in Rails Credentials.
273
+ * Generate a new secret key with `bin/rails secret` and set it as `DATO_PUBLISH_KEY` environment variable or in Rails Credentials as `dato.publish_key`.
289
274
  * Create a build trigger with a custom webhook on your Dato CMS project setting.
290
275
  * Define the Trigger URL as `https://yourapp.com/dato/publish`
291
276
  * Set the `DATO_PUBLISH_KEY` as the Authorization header
292
277
  * Copy the build trigger id and set it as `DATO_BUILD_TRIGGER_ID` environment variable.
293
278
 
279
+ ![docs/build_trigger_configuration.png](docs/build_trigger_configuration.png)
280
+
294
281
  ### Caching of graphQL response / Controller helpers
295
282
 
296
283
  If you are not using ViewComponents or you need to cache a single GraphQL query, you can still do it using the
297
284
  `Dato::Cache` helper.
298
285
 
299
- In your controllers you already have a helper method `execute_dato_query`.
286
+ In your controllers you already have a helper method `execute_dato_query!`.
300
287
  You can use it in your controller action and queries results will be automatically cached if:
301
288
 
302
289
  * You have `Dato` cache enabled
@@ -304,7 +291,7 @@ You can use it in your controller action and queries results will be automatical
304
291
  * you are not displaying a preview
305
292
 
306
293
  ```ruby
307
- response = execute_dato_query(blog_post_query(params[:slug]), preview: params[:preview])
294
+ response = execute_dato_query!(blog_post_query(params[:slug]), preview: params[:preview])
308
295
  @data = response.data
309
296
  ```
310
297
 
@@ -321,7 +308,6 @@ Completed 200 OK in 365ms (Views: 109.5ms | ActiveRecord: 0.7ms (4 queries, 0 ca
321
308
 
322
309
  this is useful to identify if the bottleneck is in the fetching of the data from Dato CMS.
323
310
 
324
-
325
311
  ## Development
326
312
 
327
313
  After checking out the repo, run `bundle install` to install dependencies.
@@ -1,13 +1,20 @@
1
- <% block = blocks.find { |b| b.id == @node.item } %>
2
- <% if block.__typename.nil? %>
3
- In order to render a block, you need to return its <code>__typename</code>.<br>
4
- In your GraphQL query, add <code>__typename</code> to the list of fields returned for the blocks.<br>
5
- For example:<br><br>
6
- <code>
7
- blocks {
8
- __typename
9
- }
10
- </code>
1
+ <% block = blocks&.find { |b| b.id == @node.item } %>
2
+ <% if block.nil? %>
3
+ <%= error_block do %>
4
+ There is a block with id <code><%= @node.item %></code> that has not been retrieved by the GraphQL Query.<br>
5
+ It's possible that your query is returning a structured text without blocks.
6
+ <% end %>
7
+ <% elsif block.__typename.nil? %>
8
+ <%= error_block do %>
9
+ In order to render a block, you need to return its <code>__typename</code>.<br>
10
+ In your GraphQL query, add <code>__typename</code> to the list of fields returned for the blocks.<br>
11
+ For example:<br><br>
12
+ <code>
13
+ blocks {
14
+ __typename
15
+ }
16
+ </code>
17
+ <% end %>
11
18
  <% else %>
12
- <%=render class_for_block(block).new(block, root) %>
19
+ <%= render class_for_block(block).new(block, root) %>
13
20
  <% end %>
@@ -13,5 +13,11 @@ module Dato
13
13
  def generated_tag
14
14
  @type
15
15
  end
16
+
17
+ private
18
+
19
+ def extract_meta(type)
20
+ @node.meta&.find { |m| m.id == type }&.value
21
+ end
16
22
  end
17
23
  end
@@ -0,0 +1,20 @@
1
+ <% block = blocks&.find { |b| b.id == @node.item } %>
2
+ <% if block.nil? %>
3
+ <%= error_block do %>
4
+ There is an inline block with id <code><%= @node.item %></code> that has not been retrieved by the GraphQL Query.<br>
5
+ It's possible that your query is returning a structured text without blocks.
6
+ <% end %>
7
+ <% elsif block.__typename.nil? %>
8
+ <%= error_block do %>
9
+ In order to render an inline block, you need to return its <code>__typename</code>.<br>
10
+ In your GraphQL query, add <code>__typename</code> to the list of fields returned for the blocks.<br>
11
+ For example:<br><br>
12
+ <code>
13
+ blocks {
14
+ __typename
15
+ }
16
+ </code>
17
+ <% end %>
18
+ <% else %>
19
+ <%= render class_for_block(block).new(block, root) %>
20
+ <% end %>
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Dato::InlineBlock < Dato::DastNode
4
+ def initialize(node, root)
5
+ super(node, "inlineBlock", root)
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ <% inline_item = links.find { |b| b.id == @node.item } %>
2
+
3
+ <% if inline_item.present? %>
4
+ <% if class_for_inline_item(inline_item).present? %>
5
+ <%= render class_for_inline_item(inline_item).new(inline_item, root) %>
6
+ <% elsif path_for_inline_item(inline_item).present? %>
7
+ <%= link_to inline_item.title, path_for_inline_item(inline_item), class: "dato-cms-#{@node.type}" %>
8
+ <% else %>
9
+ <%= error_block do %>
10
+ This is an unknown InlineItem. Here is the content:<br>
11
+ <pre><%= JSON.pretty_generate(inline_item) %></pre>
12
+ This is a link, so you have some options:
13
+ <ul>
14
+ <li>Define the function to generate path for this item. For example:<br>
15
+ <code>
16
+ config.links_mapping = {<br>
17
+ &nbsp;'<%= inline_item.__typename %>' => ->(inline_item) { blog_post_path(inline_item.slug) }<br>
18
+ }<br>
19
+ </code>
20
+ This will use the default link renderer, and set the href attribute according to your definition.
21
+ </li>
22
+ <li>
23
+ Define a class in your <code>app/components/dato</code> folder. For example:<br>
24
+ <code>
25
+ class <%= class_by_type(inline_item.__typename) %> < Dato::Node<br>
26
+ ...<br>
27
+ end<br>
28
+ </code><br>
29
+ </li>
30
+ <li>Not render it by excluding it from your GraphQL Query</li>
31
+ <li>Not render it by using the overrides: <code>{overrides: '<%= @node.__typename %>': Dato::NotRendered }</code></li>
32
+ </ul>
33
+ <% end %>
34
+ <% end %>
35
+ <% else %>
36
+ <%= error_block do %>
37
+ I don't know this inline item.
38
+ <% end %>
39
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Dato::InlineItem < Dato::Node
4
+ end
@@ -0,0 +1,18 @@
1
+ <% if inline_item.__typename.nil? %>
2
+ <%= error_block do %>
3
+ In order to render an item link, you need to return its <code>__typename</code>.<br>
4
+ In your GraphQL query, add <code>__typename</code> to the list of fields returned for the blocks.<br>
5
+ For example:<br><br>
6
+ <code>
7
+ links {
8
+ __typename
9
+ }
10
+ </code>
11
+ <% end %>
12
+ <% else %>
13
+ <%= tag.a(**link_attributes) do -%>
14
+ <% @node.children&.each do |node| -%>
15
+ <%= render_node(node) -%>
16
+ <% end -%>
17
+ <% end -%>
18
+ <% end %>
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Dato::ItemLink < Dato::DastNode
4
+ def initialize(node, root)
5
+ super(node, "itemLink", root)
6
+ end
7
+
8
+ def inline_item
9
+ links.find { |b| b.id == @node.item }
10
+ end
11
+
12
+ def link_attributes
13
+ attr = {
14
+ "href" => path_for_inline_item(inline_item),
15
+ "class" => "dato-cms-#{@node.type}"
16
+ }
17
+ %w[rel target].each { |type| attr[type] = extract_meta(type) }
18
+ attr
19
+ end
20
+ end
@@ -17,10 +17,4 @@ class Dato::Link < Dato::DastNode
17
17
  %w[rel target].each { |type| attr[type] = extract_meta(type) }
18
18
  attr
19
19
  end
20
-
21
- private
22
-
23
- def extract_meta(type)
24
- @node.meta&.find { |m| m.id == type }&.value
25
- end
26
20
  end
@@ -17,6 +17,10 @@ module Dato
17
17
  root.blocks
18
18
  end
19
19
 
20
+ def links
21
+ root.links
22
+ end
23
+
20
24
  def overrides
21
25
  root.overrides
22
26
  end
@@ -39,6 +43,25 @@ module Dato
39
43
  end
40
44
  end
41
45
 
46
+ def class_for_inline_item(inline_item)
47
+ class_name = overrides[inline_item.__typename] || Dato::Config.overrides[inline_item.__typename]
48
+ if class_name.is_a?(String)
49
+ class_name = class_name.constantize
50
+ end
51
+ begin
52
+ class_name || class_by_type(inline_item.__typename).constantize
53
+ rescue NameError
54
+ nil
55
+ end
56
+ end
57
+
58
+ def path_for_inline_item(inline_item)
59
+ lambda = Dato::Config.links_mapping[inline_item.__typename]
60
+ if lambda
61
+ instance_exec(inline_item, &lambda)
62
+ end
63
+ end
64
+
42
65
  def class_for_node(node)
43
66
  class_name = overrides[node.type] || Dato::Config.overrides[node.type]
44
67
  if class_name.is_a?(String)
@@ -54,5 +77,10 @@ module Dato
54
77
  def class_by_type(type)
55
78
  "Dato::#{type.classify}"
56
79
  end
80
+
81
+ def error_block(&block)
82
+ content = capture(&block)
83
+ content_tag(:div, content, style: "border: 3px dotted limegreen; display: inline-block; padding: 5px;font-weight: bold")
84
+ end
57
85
  end
58
86
  end
@@ -6,6 +6,6 @@ class Dato::Paragraph < Dato::DastNode
6
6
  end
7
7
 
8
8
  def generated_tag
9
- "p"
9
+ "div"
10
10
  end
11
11
  end
@@ -13,6 +13,7 @@ class Dato::StructuredText < Dato::Node
13
13
  @overrides = overrides.with_indifferent_access
14
14
  @structured_text_node = structured_text_node
15
15
  @blocks = structured_text_node.blocks
16
+ @links = structured_text_node.links
16
17
  end
17
18
 
18
19
  def root
@@ -20,6 +21,7 @@ class Dato::StructuredText < Dato::Node
20
21
  end
21
22
 
22
23
  attr_reader :blocks
24
+ attr_reader :links
23
25
 
24
26
  attr_reader :overrides
25
27
  end
@@ -1,17 +1,18 @@
1
- <div style="border: 3px dotted limegreen; display: inline-block; padding: 5px;">
2
- <b>
3
- This is an unknown block. Here is the content:<br>
4
- <pre><%= JSON.pretty_generate(@node) %></pre>
5
- In order to render it, you need to define a ViewComponent named <code><%=class_by_type(@node.__typename) %></code>.<br>
6
- You can define this class in your <code>app/components/dato</code> folder. For example:<br><br>
7
- <code>
8
- class <%=class_by_type(@node.__typename) %> < Dato::DastNode<br>
9
- ...<br>
10
- end<br>
11
- </code><br>
12
- If you don't want to render this block you can:
13
- * not return it in your GraphQL query
14
- * the component when rendering the root <code>Dato::StructuredText</code> node.<br>
15
- <code>Dato::StructuredText.new(content, overrides: { '<%=@node.__typename %>': Dato::NotRendered })</code>
16
- </b>
17
- </div>
1
+ <%= error_block do %>
2
+ This is an unknown block. Here is the content:<br>
3
+ <pre><%= JSON.pretty_generate(@node) %></pre>
4
+ In order to render it, you need to define a ViewComponent named <code><%= class_by_type(@node.__typename) %></code>.
5
+ <br>
6
+ You can define this class in your <code>app/components/dato</code> folder. For example:<br><br>
7
+ <code>
8
+ class <%= class_by_type(@node.__typename) %> < Dato::Node<br>
9
+ ...<br>
10
+ end<br>
11
+ </code><br>
12
+ If you don't want to render this block you can:
13
+ <ul>
14
+ <li>not return it in your GraphQL query</li>
15
+ <li>override the component when rendering the root <code>Dato::StructuredText</code> node.<br>
16
+ <code>Dato::StructuredText.new(content, overrides: { '<%= @node.__typename %>': Dato::NotRendered })</code></li>
17
+ </ul>
18
+ <% end %>
@@ -1,15 +1,15 @@
1
- <div style="border: 3px dotted limegreen; display: inline-block; padding: 5px;">
2
- <b>
3
- This is an unknown node of type <code><%=@type %></code>.<br>
4
- In order to render it, you need to define a ViewComponent named <code><%=class_by_type(@node.type) %></code>.<br>
5
- You can define this class in your <code>app/components/dato</code> folder. For example:<br><br>
6
- <code>
7
- class <%=class_by_type(@node.type) %> < Dato::DastNode<br>
8
- ...<br>
9
- end<br>
10
- </code><br>
11
- If you don't want to render this node, you can override the component when rendering the root <code>Dato::StructuredText</code> node.<br>
12
- <code>Dato::StructuredText.new(content, overrides: { link: Dato::NotRendered })</code>
13
- </b>
1
+ <%= error_block do %>
2
+ This is an unknown node of type <code><%= @type %></code>. Here is the content:<br>
3
+ <pre><%= JSON.pretty_generate(@node) %></pre>
4
+ In order to render it, you need to define a ViewComponent named <code><%= class_by_type(@node.type) %></code>.<br>
5
+ You can define this class in your <code>app/components/dato</code> folder. For example:<br><br>
6
+ <code>
7
+ class <%= class_by_type(@node.type) %> < Dato::Node<br>
8
+ ...<br>
9
+ end<br>
10
+ </code><br>
11
+ If you don't want to render this node, you can override the component when rendering the root
12
+ <code>Dato::StructuredText</code> node.<br>
13
+ <code>Dato::StructuredText.new(content, overrides: { link: Dato::NotRendered })</code>
14
14
  <%= debug_node %>
15
- </div>
15
+ <% end %>
data/lib/dato/config.rb CHANGED
@@ -3,12 +3,14 @@ module Dato
3
3
  include ActiveSupport::Configurable
4
4
 
5
5
  config_accessor(:overrides) { {} }
6
+ config_accessor(:links_mapping) { {} }
6
7
  config_accessor(:blocks) { {} }
7
8
  config_accessor(:cache) { false }
8
9
  config_accessor(:cache_namespace) { "dato-rails" }
9
10
  config_accessor(:api_token) { ENV.fetch("DATO_API_TOKEN", Rails.application.credentials.dig(:dato, :api_token)) }
10
11
  config_accessor(:publish_key) { ENV.fetch("DATO_PUBLISH_KEY", Rails.application.credentials.dig(:dato, :publish_key)) }
11
12
  config_accessor(:build_trigger_id) { ENV.fetch("DATO_BUILD_TRIGGER_ID", Rails.application.credentials.dig(:dato, :build_trigger_id)) }
13
+ config_accessor(:base_editing_url) { ENV.fetch("DATO_BASE_EDITING_URL", Rails.application.credentials.dig(:dato, :base_editing_url)) }
12
14
  config_accessor(:mount_path) { "/dato" }
13
15
  end
14
16
  end
data/lib/dato/engine.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  module Dato
2
+ class InvalidGraphqlQuery < StandardError; end
3
+
2
4
  class Engine < ::Rails::Engine
3
5
  isolate_namespace Dato
4
6
 
data/lib/dato/gql.rb CHANGED
@@ -2,11 +2,12 @@ module Dato
2
2
  class Gql < GQLi::Client
3
3
  def initialize(api_token, validate_query, preview, live)
4
4
  @api_token = api_token
5
+ headers = {"Authorization" => @api_token}
6
+ headers["X-Base-Editing-Url"] = Dato::Config.base_editing_url if Dato::Config.base_editing_url.present?
7
+
5
8
  super(
6
9
  "https://graphql#{"-listen" if live}.datocms.com/#{"preview" if preview}",
7
- headers: {
8
- "Authorization" => @api_token
9
- },
10
+ headers: headers,
10
11
  validate_query: validate_query && !live
11
12
  )
12
13
  end
@@ -5,9 +5,7 @@ module Dato
5
5
 
6
6
  def execute_dato_query(query, preview: false)
7
7
  client = Dato::Client.new(preview: preview)
8
- if preview
9
- return client.execute!(query)
10
- end
8
+ return client.execute!(query) if preview
11
9
 
12
10
  key = "#{Digest::MD5.hexdigest(query.to_gql)}-query-#{preview}"
13
11
  Dato::Cache.fetch(key) do
@@ -15,6 +13,13 @@ module Dato
15
13
  end
16
14
  end
17
15
 
16
+ def execute_dato_query!(query, preview: false)
17
+ response = execute_dato_query(query, preview: preview)
18
+ raise(InvalidGraphqlQuery, response.errors.first.message) if response.errors&.any?
19
+
20
+ response
21
+ end
22
+
18
23
  module ClassMethods # :nodoc:
19
24
  def log_process_action(payload)
20
25
  messages = super
data/lib/dato/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dato
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dato-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-25 00:00:00.000000000 Z
10
+ date: 2025-02-27 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rspec-rails
@@ -186,6 +186,12 @@ files:
186
186
  - app/components/dato/heading.rb
187
187
  - app/components/dato/image_block_record.html.erb
188
188
  - app/components/dato/image_block_record.rb
189
+ - app/components/dato/inline_block.html.erb
190
+ - app/components/dato/inline_block.rb
191
+ - app/components/dato/inline_item.html.erb
192
+ - app/components/dato/inline_item.rb
193
+ - app/components/dato/item_link.html.erb
194
+ - app/components/dato/item_link.rb
189
195
  - app/components/dato/link.html.erb
190
196
  - app/components/dato/link.rb
191
197
  - app/components/dato/list.rb