dato 0.7.18 → 0.8.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +42 -5
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +2 -2
  5. data/Gemfile +1 -1
  6. data/Rakefile +2 -2
  7. data/TODO.md +28 -0
  8. data/bin/console +3 -3
  9. data/bin/rspec +6 -6
  10. data/dato.gemspec +1 -1
  11. data/exe/dato +3 -3
  12. data/lib/dato/account/client.rb +2 -2
  13. data/lib/dato/api_client.rb +37 -43
  14. data/lib/dato/api_error.rb +10 -13
  15. data/lib/dato/cli.rb +18 -18
  16. data/lib/dato/dump/dsl/add_to_data_file.rb +1 -1
  17. data/lib/dato/dump/dsl/create_data_file.rb +1 -1
  18. data/lib/dato/dump/dsl/create_post.rb +1 -1
  19. data/lib/dato/dump/dsl/directory.rb +4 -4
  20. data/lib/dato/dump/dsl/root.rb +7 -7
  21. data/lib/dato/dump/format/json.rb +1 -1
  22. data/lib/dato/dump/format/toml.rb +5 -6
  23. data/lib/dato/dump/format/yaml.rb +2 -3
  24. data/lib/dato/dump/format.rb +3 -3
  25. data/lib/dato/dump/operation/add_to_data_file.rb +4 -4
  26. data/lib/dato/dump/operation/create_data_file.rb +3 -3
  27. data/lib/dato/dump/operation/create_post.rb +5 -6
  28. data/lib/dato/dump/operation/directory.rb +1 -1
  29. data/lib/dato/dump/runner.rb +5 -5
  30. data/lib/dato/dump/ssg_detector.rb +18 -20
  31. data/lib/dato/json_api_deserializer.rb +15 -16
  32. data/lib/dato/json_api_serializer.rb +39 -28
  33. data/lib/dato/json_schema_relationships.rb +19 -23
  34. data/lib/dato/json_schema_type.rb +47 -0
  35. data/lib/dato/local/entities_repo.rb +3 -3
  36. data/lib/dato/local/field_type/color.rb +11 -7
  37. data/lib/dato/local/field_type/file.rb +18 -24
  38. data/lib/dato/local/field_type/gallery.rb +1 -1
  39. data/lib/dato/local/field_type/global_seo.rb +4 -7
  40. data/lib/dato/local/field_type/lat_lon.rb +1 -1
  41. data/lib/dato/local/field_type/seo.rb +1 -1
  42. data/lib/dato/local/field_type/structured_text.rb +63 -0
  43. data/lib/dato/local/field_type/theme.rb +2 -2
  44. data/lib/dato/local/field_type/upload_id.rb +5 -5
  45. data/lib/dato/local/field_type/video.rb +9 -15
  46. data/lib/dato/local/item.rb +11 -12
  47. data/lib/dato/local/items_repo.rb +11 -18
  48. data/lib/dato/local/json_api_entity.rb +4 -3
  49. data/lib/dato/local/loader.rb +30 -31
  50. data/lib/dato/local/site.rb +3 -4
  51. data/lib/dato/paginator.rb +4 -4
  52. data/lib/dato/repo.rb +23 -30
  53. data/lib/dato/site/client.rb +5 -5
  54. data/lib/dato/upload/create_upload_path.rb +7 -10
  55. data/lib/dato/upload/file.rb +3 -3
  56. data/lib/dato/upload/image.rb +1 -1
  57. data/lib/dato/utils/build_modular_block.rb +4 -4
  58. data/lib/dato/utils/favicon_tags_builder.rb +10 -10
  59. data/lib/dato/utils/locale_value.rb +1 -1
  60. data/lib/dato/utils/meta_tags/article_modified_time.rb +3 -3
  61. data/lib/dato/utils/meta_tags/article_publisher.rb +2 -2
  62. data/lib/dato/utils/meta_tags/base.rb +5 -6
  63. data/lib/dato/utils/meta_tags/description.rb +4 -4
  64. data/lib/dato/utils/meta_tags/image.rb +4 -5
  65. data/lib/dato/utils/meta_tags/og_locale.rb +2 -2
  66. data/lib/dato/utils/meta_tags/og_site_name.rb +2 -2
  67. data/lib/dato/utils/meta_tags/og_type.rb +3 -3
  68. data/lib/dato/utils/meta_tags/robots.rb +2 -2
  69. data/lib/dato/utils/meta_tags/title.rb +6 -6
  70. data/lib/dato/utils/meta_tags/twitter_card.rb +2 -2
  71. data/lib/dato/utils/meta_tags/twitter_site.rb +2 -2
  72. data/lib/dato/utils/seo_tags_builder.rb +12 -12
  73. data/lib/dato/version.rb +1 -1
  74. data/lib/dato.rb +11 -9
  75. metadata +10 -8
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'imgix'
3
+ require "imgix"
4
4
 
5
5
  module Dato
6
6
  module Local
@@ -10,7 +10,7 @@ module Dato
10
10
  if value
11
11
  v = value.with_indifferent_access
12
12
 
13
- upload = repo.entities_repo.find_entity('upload', v[:upload_id])
13
+ upload = repo.entities_repo.find_entity("upload", v[:upload_id])
14
14
 
15
15
  if upload
16
16
  new(
@@ -19,7 +19,7 @@ module Dato
19
19
  v[:title],
20
20
  v[:custom_data],
21
21
  v[:focal_point],
22
- repo.site.entity.imgix_host
22
+ repo.site.entity.imgix_host,
23
23
  )
24
24
  end
25
25
  end
@@ -88,25 +88,25 @@ module Dato
88
88
  def alt
89
89
  default_metadata = @upload.default_field_metadata.deep_stringify_keys
90
90
  .fetch(I18n.locale.to_s, {})
91
- @alt || default_metadata['alt']
91
+ @alt || default_metadata["alt"]
92
92
  end
93
93
 
94
94
  def title
95
95
  default_metadata = @upload.default_field_metadata.deep_stringify_keys
96
96
  .fetch(I18n.locale.to_s, {})
97
- @title || default_metadata['title']
97
+ @title || default_metadata["title"]
98
98
  end
99
99
 
100
100
  def custom_data
101
101
  default_metadata = @upload.default_field_metadata.deep_stringify_keys
102
102
  .fetch(I18n.locale.to_s, {})
103
- @custom_data.merge(default_metadata.fetch('custom_data', {}))
103
+ @custom_data.merge(default_metadata.fetch("custom_data", {}))
104
104
  end
105
105
 
106
106
  def focal_point
107
107
  default_metadata = @upload.default_field_metadata.deep_stringify_keys
108
108
  .fetch(I18n.locale.to_s, {})
109
- @focal_point || default_metadata['focal_point']
109
+ @focal_point || default_metadata["focal_point"]
110
110
  end
111
111
 
112
112
  def tags
@@ -174,9 +174,7 @@ module Dato
174
174
  if options[:exact_res] == :low
175
175
  raw_mp4_url("low")
176
176
  elsif options[:exact_res] == :medium
177
- if %w[medium high].include?(@upload.mux_mp4_highest_res)
178
- raw_mp4_url("medium")
179
- end
177
+ raw_mp4_url("medium") if %w[medium high].include?(@upload.mux_mp4_highest_res)
180
178
  elsif @upload.mux_mp4_highest_res == :high
181
179
  raw_mp4_url("high")
182
180
  end
@@ -212,16 +210,14 @@ module Dato
212
210
  end
213
211
 
214
212
  def video
215
- if @upload.mux_playback_id
216
- VideoAttributes.new(@upload)
217
- end
213
+ VideoAttributes.new(@upload) if @upload.mux_playback_id
218
214
  end
219
215
 
220
216
  def file
221
217
  Imgix::Client.new(
222
218
  domain: @imgix_host,
223
219
  secure: true,
224
- include_library_param: false
220
+ include_library_param: false,
225
221
  ).path(path)
226
222
  end
227
223
 
@@ -229,12 +225,12 @@ module Dato
229
225
  query.deep_stringify_keys!
230
226
 
231
227
  if focal_point &&
232
- query["fit"] == "crop" &&
233
- (query["h"] || query["height"]) &&
234
- (query["w"] || query["width"]) &&
235
- [nil, "focalpoint"].include?(query["crop"]) &&
236
- query["fp-x"].nil? &&
237
- query["fp-y"].nil?
228
+ query["fit"] == "crop" &&
229
+ (query["h"] || query["height"]) &&
230
+ (query["w"] || query["width"]) &&
231
+ [nil, "focalpoint"].include?(query["crop"]) &&
232
+ query["fp-x"].nil? &&
233
+ query["fp-y"].nil?
238
234
 
239
235
  query.merge!(
240
236
  "crop" => "focalpoint",
@@ -252,9 +248,7 @@ module Dato
252
248
 
253
249
  response = Faraday.get(file.to_url(opts.merge(lqip: "blurhash")))
254
250
 
255
- if response.status == 200
256
- "data:image/jpeg;base64,#{Base64.strict_encode64(response.body)}"
257
- end
251
+ "data:image/jpeg;base64,#{Base64.strict_encode64(response.body)}" if response.status == 200
258
252
  end
259
253
 
260
254
  def to_hash(*_args)
@@ -279,7 +273,7 @@ module Dato
279
273
  mime_type: mime_type,
280
274
  colors: colors.map(&:to_hash),
281
275
  blurhash: blurhash,
282
- video: video && video.to_hash
276
+ video: video && video.to_hash,
283
277
  }
284
278
  end
285
279
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dato/local/field_type/file'
3
+ require "dato/local/field_type/file"
4
4
 
5
5
  module Dato
6
6
  module Local
@@ -1,15 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dato/local/field_type/seo'
3
+ require "dato/local/field_type/seo"
4
4
 
5
5
  module Dato
6
6
  module Local
7
7
  module FieldType
8
8
  class GlobalSeo
9
- attr_reader :site_name
10
- attr_reader :title_suffix
11
- attr_reader :twitter_account
12
- attr_reader :facebook_page_url
9
+ attr_reader :site_name, :title_suffix, :twitter_account, :facebook_page_url
13
10
 
14
11
  def self.parse(value, repo)
15
12
  value && new(
@@ -18,7 +15,7 @@ module Dato
18
15
  value[:twitter_account],
19
16
  value[:facebook_page_url],
20
17
  value[:fallback_seo],
21
- repo
18
+ repo,
22
19
  )
23
20
  end
24
21
 
@@ -48,7 +45,7 @@ module Dato
48
45
  title_suffix: title_suffix,
49
46
  twitter_account: twitter_account,
50
47
  facebook_page_url: facebook_page_url,
51
- fallback_seo: fallback_seo && fallback_seo.to_hash(*args)
48
+ fallback_seo: fallback_seo && fallback_seo.to_hash(*args),
52
49
  }
53
50
  end
54
51
  end
@@ -22,7 +22,7 @@ module Dato
22
22
  def to_hash(*_args)
23
23
  {
24
24
  latitude: latitude,
25
- longitude: longitude
25
+ longitude: longitude,
26
26
  }
27
27
  end
28
28
  end
@@ -26,7 +26,7 @@ module Dato
26
26
  {
27
27
  title: title,
28
28
  description: description,
29
- image: image && image.to_hash(*args)
29
+ image: image && image.to_hash(*args),
30
30
  }
31
31
  end
32
32
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dato
4
+ module Local
5
+ module FieldType
6
+ class StructuredText
7
+ def self.parse(value, repo)
8
+ new(value, repo)
9
+ end
10
+
11
+ def initialize(value, repo)
12
+ @value = value
13
+ @repo = repo
14
+ end
15
+
16
+ attr_reader :value
17
+
18
+ def blocks
19
+ find_all_nodes("block").map do |node|
20
+ @repo.find(node["item"])
21
+ end.uniq
22
+ end
23
+
24
+ def links
25
+ find_all_nodes(%w[inlineItem itemLink]).map do |node|
26
+ @repo.find(node["item"])
27
+ end.uniq
28
+ end
29
+
30
+ def find_all_nodes(types)
31
+ return [] if value.nil?
32
+
33
+ types = Array(types)
34
+ result = []
35
+
36
+ visit(value["document"]) do |node|
37
+ result << node if node.is_a?(Hash) && types.include?(node["type"])
38
+ end
39
+
40
+ result
41
+ end
42
+
43
+ def visit(node, &block)
44
+ if node.is_a?(Hash) && node["children"].is_a?(Array)
45
+ node["children"].each do |child|
46
+ visit(child, &block)
47
+ end
48
+ end
49
+
50
+ block.call(node)
51
+ end
52
+
53
+ def to_hash(max_depth = 3, current_depth = 0)
54
+ {
55
+ value: value,
56
+ links: links.map { |item| item.to_hash(max_depth, current_depth) },
57
+ blocks: blocks.map { |item| item.to_hash(max_depth, current_depth) },
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -13,7 +13,7 @@ module Dato
13
13
  value[:dark_color],
14
14
  value[:light_color],
15
15
  value[:accent_color],
16
- repo
16
+ repo,
17
17
  )
18
18
  end
19
19
 
@@ -36,7 +36,7 @@ module Dato
36
36
  dark_color: dark_color,
37
37
  light_color: light_color,
38
38
  accent_color: accent_color,
39
- logo: logo && logo.to_hash(*args)
39
+ logo: logo && logo.to_hash(*args),
40
40
  }
41
41
  end
42
42
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'imgix'
3
+ require "imgix"
4
4
 
5
5
  module Dato
6
6
  module Local
@@ -10,7 +10,7 @@ module Dato
10
10
 
11
11
  def self.parse(upload_id, repo)
12
12
  if upload_id
13
- upload = repo.entities_repo.find_entity('upload', upload_id)
13
+ upload = repo.entities_repo.find_entity("upload", upload_id)
14
14
 
15
15
  if upload
16
16
  new(
@@ -19,7 +19,7 @@ module Dato
19
19
  upload.size,
20
20
  upload.width,
21
21
  upload.height,
22
- repo.site.entity.imgix_host
22
+ repo.site.entity.imgix_host,
23
23
  )
24
24
  end
25
25
  end
@@ -45,7 +45,7 @@ module Dato
45
45
  Imgix::Client.new(
46
46
  domain: @imgix_host,
47
47
  secure: true,
48
- include_library_param: false
48
+ include_library_param: false,
49
49
  ).path(path)
50
50
  end
51
51
 
@@ -59,7 +59,7 @@ module Dato
59
59
  size: size,
60
60
  width: width,
61
61
  height: height,
62
- url: url
62
+ url: url,
63
63
  }
64
64
  end
65
65
  end
@@ -4,14 +4,7 @@ module Dato
4
4
  module Local
5
5
  module FieldType
6
6
  class Video
7
- attr_reader :url
8
- attr_reader :thumbnail_url
9
- attr_reader :title
10
- attr_reader :width
11
- attr_reader :height
12
- attr_reader :provider
13
- attr_reader :provider_url
14
- attr_reader :provider_uid
7
+ attr_reader :url, :thumbnail_url, :title, :width, :height, :provider, :provider_url, :provider_uid
15
8
 
16
9
  def self.parse(value, _repo)
17
10
  value && new(
@@ -22,7 +15,7 @@ module Dato
22
15
  value[:height],
23
16
  value[:provider],
24
17
  value[:provider_url],
25
- value[:provider_uid]
18
+ value[:provider_uid],
26
19
  )
27
20
  end
28
21
 
@@ -47,15 +40,16 @@ module Dato
47
40
  end
48
41
 
49
42
  def iframe_embed(width = self.width, height = self.height)
50
- # rubocop:disable Metrics/LineLength
51
- if provider == 'youtube'
43
+ # rubocop:disable Layout/LineLength
44
+ case provider
45
+ when "youtube"
52
46
  %(<iframe width="#{width}" height="#{height}" src="//www.youtube.com/embed/#{provider_uid}?rel=0" frameborder="0" allowfullscreen></iframe>)
53
- elsif provider == 'vimeo'
47
+ when "vimeo"
54
48
  %(<iframe src="//player.vimeo.com/video/#{provider_uid}?title=0&amp;byline=0&amp;portrait=0" width="#{width}" height="#{height}" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>)
55
- elsif provider == 'facebook'
49
+ when "facebook"
56
50
  %(<iframe src="//www.facebook.com/plugins/video.php?href=#{url}&width=#{width}&show_text=false&height=#{height}" width="#{width}" height="#{height}" style="border:none;overflow:hidden;width:100%;" scrolling="no" frameborder="0" allowTransparency="true" allow="encrypted-media" allowFullScreen="true"></iframe>)
57
51
  end
58
- # rubocop:enable Metrics/LineLength
52
+ # rubocop:enable Layout/LineLength
59
53
  end
60
54
 
61
55
  def to_hash(*_args)
@@ -67,7 +61,7 @@ module Dato
67
61
  height: height,
68
62
  provider: provider,
69
63
  provider_url: provider_url,
70
- provider_uid: provider_uid
64
+ provider_uid: provider_uid,
71
65
  }
72
66
  end
73
67
  end
@@ -1,11 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
4
- require 'active_support/inflector/transliterate'
5
- require 'active_support/hash_with_indifferent_access'
6
- require 'dato/utils/locale_value'
3
+ require "forwardable"
4
+ require "dato/utils/locale_value"
7
5
 
8
- Dir[File.dirname(__FILE__) + '/field_type/*.rb'].each do |file|
6
+ Dir["#{File.dirname(__FILE__)}/field_type/*.rb"].sort.each do |file|
9
7
  require file
10
8
  end
11
9
 
@@ -15,6 +13,7 @@ module Dato
15
13
  extend Forwardable
16
14
 
17
15
  attr_reader :entity
16
+
18
17
  def_delegators :entity, :id, :meta
19
18
 
20
19
  def initialize(entity, items_repo)
@@ -45,7 +44,7 @@ module Dato
45
44
 
46
45
  def attributes
47
46
  fields.each_with_object(
48
- ActiveSupport::HashWithIndifferentAccess.new
47
+ ActiveSupport::HashWithIndifferentAccess.new,
49
48
  ) do |field, acc|
50
49
  acc[field.api_key.to_sym] = read_attribute(field.api_key, field)
51
50
  end
@@ -88,7 +87,7 @@ module Dato
88
87
  id: id,
89
88
  item_type: item_type.api_key,
90
89
  updated_at: updated_at,
91
- created_at: created_at
90
+ created_at: created_at,
92
91
  }
93
92
 
94
93
  base[:position] = position if item_type.sortable
@@ -98,7 +97,7 @@ module Dato
98
97
  base[:children] = children.map do |child|
99
98
  child.to_hash(
100
99
  max_depth,
101
- current_depth + 1
100
+ current_depth + 1,
102
101
  )
103
102
  end
104
103
  end
@@ -109,7 +108,7 @@ module Dato
109
108
  result[field.api_key.to_sym] = if value.respond_to?(:to_hash)
110
109
  value.to_hash(
111
110
  max_depth,
112
- current_depth + 1
111
+ current_depth + 1,
113
112
  )
114
113
  else
115
114
  value
@@ -138,9 +137,9 @@ module Dato
138
137
  "Warning: unrecognized field of type `#{field_type}`",
139
138
  "for item `#{item_type.api_key}` and",
140
139
  "field `#{method}`: returning a simple Hash instead.",
141
- 'Please upgrade to the latest version of the `dato` gem!'
140
+ "Please upgrade to the latest version of the `dato` gem!",
142
141
  ]
143
- puts warning.join(' ')
142
+ puts warning.join(" ")
144
143
 
145
144
  value
146
145
  end
@@ -154,7 +153,7 @@ module Dato
154
153
  super
155
154
  end
156
155
  rescue NoMethodError => e
157
- if e.name === method
156
+ if e.name == method
158
157
  message = []
159
158
  message << "Undefined method `#{method}`"
160
159
  message << "Available fields for a `#{item_type.api_key}` item:"
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/string'
4
- require 'dato/local/item'
5
- require 'dato/local/site'
3
+ require "dato/local/item"
4
+ require "dato/local/site"
6
5
 
7
6
  module Dato
8
7
  module Local
@@ -37,8 +36,8 @@ module Dato
37
36
 
38
37
  def site
39
38
  Site.new(
40
- entities_repo.find_entities_of_type('site').first,
41
- self
39
+ entities_repo.find_entities_of_type("site").first,
40
+ self,
42
41
  )
43
42
  end
44
43
 
@@ -47,7 +46,7 @@ module Dato
47
46
  end
48
47
 
49
48
  def item_types
50
- entities_repo.find_entities_of_type('item_type')
49
+ entities_repo.find_entities_of_type("item_type")
51
50
  end
52
51
 
53
52
  def single_instance_item_types
@@ -97,7 +96,7 @@ module Dato
97
96
  end
98
97
 
99
98
  if clashing_keys.include?(pluralized_api_key)
100
- suffix = item_type.singleton ? 'instance' : 'collection'
99
+ suffix = item_type.singleton ? "instance" : "collection"
101
100
  method = "#{method}_#{suffix}"
102
101
  end
103
102
 
@@ -119,9 +118,7 @@ module Dato
119
118
  item = Item.new(item_entity, self)
120
119
  method = item_type_methods[item_entity.item_type]
121
120
 
122
- unless item_entity.item_type.singleton
123
- @collections_by_type[method].push item
124
- end
121
+ @collections_by_type[method].push item unless item_entity.item_type.singleton
125
122
 
126
123
  @items_by_id[item.id] = item
127
124
 
@@ -144,9 +141,7 @@ module Dato
144
141
  item[field].nil?
145
142
  end
146
143
  @collections_by_type[method] = valid_items.sort_by { |item| item[field] } + nil_items
147
- if item_type.ordering_direction == 'desc'
148
- @collections_by_type[method].reverse!
149
- end
144
+ @collections_by_type[method].reverse! if item_type.ordering_direction == "desc"
150
145
  end
151
146
  end
152
147
  end
@@ -156,16 +151,14 @@ module Dato
156
151
  method = item_type_methods[item_type]
157
152
  next unless item_type.singleton
158
153
 
159
- item = if item_type.singleton_item
160
- @items_by_id[item_type.singleton_item.id]
161
- end
154
+ item = (@items_by_id[item_type.singleton_item.id] if item_type.singleton_item)
162
155
 
163
156
  @collections_by_type[method] = item
164
157
  end
165
158
  end
166
159
 
167
160
  def item_entities
168
- entities_repo.find_entities_of_type('item')
161
+ entities_repo.find_entities_of_type("item")
169
162
  end
170
163
 
171
164
  def method_missing(method, *arguments, &block)
@@ -177,7 +170,7 @@ module Dato
177
170
  rescue NoMethodError
178
171
  message = []
179
172
  message << "Undefined method `#{method}`"
180
- message << 'Available DatoCMS collections/items:'
173
+ message << "Available DatoCMS collections/items:"
181
174
  message += collections_by_type.map do |key, _value|
182
175
  "* .#{key}"
183
176
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dato/local/json_api_meta'
3
+ require "dato/local/json_api_meta"
4
4
 
5
5
  module Dato
6
6
  module Local
@@ -60,11 +60,12 @@ module Dato
60
60
  end
61
61
 
62
62
  def dereference_linkage(linkage)
63
- if linkage.is_a? Array
63
+ case linkage
64
+ when Array
64
65
  linkage.map do |item|
65
66
  data_source.find_entity(item[:type], item[:id])
66
67
  end
67
- elsif linkage.is_a? Hash
68
+ when Hash
68
69
  data_source.find_entity(linkage[:type], linkage[:id])
69
70
  end
70
71
  end