dato 0.1.31 → 0.2.4

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
  SHA1:
3
- metadata.gz: 3e6ffeb74c86a321901eeaf73084308fa26e0181
4
- data.tar.gz: 6fd4ca5ad2a9f8fdb846970ac065603740c19436
3
+ metadata.gz: b54b098b276ed1157ddb3a025a7ed159f4de08ce
4
+ data.tar.gz: 51dabe629a57a274820cf257173b06cf0e3f48fe
5
5
  SHA512:
6
- metadata.gz: 1870915a900d2e7877acba29fbb74cf4a578bda4a21b2566959f1d5189431514b43dd61e2d5d601d5e42f95cc0caf76410660a908120cb23fcda6a9815a4f88b
7
- data.tar.gz: ff7093aeeb4cbadb4bec71fae0066bedbeb6e5fa37fe11e1e867eefaff58d06354063c2a69e33d468c77e7a22e7cccc3132753e840f5e0b9dcfab0903e16f025
6
+ metadata.gz: c175e119234ebb14bdbd534b68a76300cffd7fa893255d95afe2f785020c925b8afce747c94141da9984fafddfbc0cd054fc221fc4f3568847709fc32c65b237
7
+ data.tar.gz: 63acf4d92d062a34301091d45402a254b08eadd1eee5ed4b6b91aaa852ec72dcab93edabdd28f3528a67736a1e90219f8616e3aeb76ae15fb3691002724657da
@@ -13,8 +13,14 @@ Metrics/ClassLength:
13
13
  Metrics/ModuleLength:
14
14
  Enabled: false
15
15
 
16
+ Metrics/CyclomaticComplexity:
17
+ Max: 7
18
+
16
19
  Metrics/AbcSize:
17
- Max: 20
20
+ Max: 30
21
+
22
+ Metrics/PerceivedComplexity:
23
+ Max: 8
18
24
 
19
25
  Metrics/LineLength:
20
26
  Exclude:
@@ -1 +1 @@
1
- 2.1.0
1
+ 2.2.2
@@ -103,12 +103,14 @@ Our job is to generate the Markdown files in the `content` directory from the da
103
103
  Using the DatoCMS web interface, we first create the following Item types:
104
104
 
105
105
  * post
106
- - title (string, required, title)
106
+ - title (string, required)
107
+ - slug (slug, required)
107
108
  - publication_date (date, required)
108
109
  - body (text, required)
109
110
 
110
111
  * author
111
112
  - name (string, required, title)
113
+ - slug (slug, required)
112
114
  - bio (text, required)
113
115
 
114
116
  * quote
@@ -217,7 +219,7 @@ end
217
219
 
218
220
  directory "data/authors" do
219
221
  dato.authors.each do |item|
220
- create_data_file "#{item.name.slug}.toml", :toml, {
222
+ create_data_file "#{item.slug}.toml", :toml, {
221
223
  name: item.name,
222
224
  bio: item.bio
223
225
  }
@@ -233,11 +235,6 @@ If a Item Type is marked as "single instance" (ie. `about_page`) you don't need
233
235
 
234
236
  You can query an item's field value with a method called like the field API identifier.
235
237
 
236
- An item also features a `.slug` method:
237
-
238
- * if an item type has a field of type `string` with a "Title" Presentation mode, than the method returns the slugified version of the title itself;
239
- * otherwise, it just returns the unique identifier of the item;
240
-
241
238
  Complex field types (ie. `image`, `file`, `video`, `seo`) implement specific methods you can use as well within the config file:
242
239
 
243
240
  ```
data/exe/dato CHANGED
@@ -4,4 +4,4 @@
4
4
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
5
5
 
6
6
  require 'dato'
7
- Dato::Dump::Cli.start(ARGV)
7
+ Dato::Cli.start(ARGV)
@@ -4,7 +4,7 @@ require 'dato/version'
4
4
  require 'dato/site/client'
5
5
  require 'dato/account/client'
6
6
  require 'dato/local/site'
7
- require 'dato/dump/cli'
7
+ require 'dato/cli'
8
8
 
9
9
  module Dato
10
10
  end
@@ -12,8 +12,12 @@ module Dato
12
12
  'DatoCMS API Error',
13
13
  "Status: #{faraday_error.response[:status]}",
14
14
  'Response:',
15
- JSON.pretty_generate(JSON.load(faraday_error.response[:body]))
15
+ JSON.pretty_generate(body)
16
16
  ].join("\n")
17
17
  end
18
+
19
+ def body
20
+ JSON.parse(faraday_error.response[:body])
21
+ end
18
22
  end
19
23
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ require 'thor'
3
+ require 'dato/dump/runner'
4
+ require 'dato/dump/ssg_detector'
5
+ require 'dato/migrate_slugs/runner'
6
+
7
+ module Dato
8
+ class Cli < Thor
9
+ package_name 'DatoCMS'
10
+
11
+ desc 'dump', 'dumps DatoCMS contents into local files'
12
+ option :config, default: 'dato.config.rb'
13
+ option :token, default: ENV['DATO_API_TOKEN'], required: true
14
+ def dump
15
+ config_file = File.expand_path(options[:config])
16
+
17
+ client = Dato::Site::Client.new(
18
+ options[:token],
19
+ extra_headers: {
20
+ 'X-Reason' => 'dump',
21
+ 'X-SSG' => Dump::SsgDetector.new(Dir.pwd).detect
22
+ }
23
+ )
24
+
25
+ Dump::Runner.new(config_file, client).run
26
+ end
27
+
28
+ desc 'migrate-slugs', 'migrates a Site so that it uses slug fields'
29
+ option :token, default: ENV['DATO_API_TOKEN'], required: true
30
+ option :skip_id_prefix, type: :boolean
31
+ def migrate_slugs
32
+ client = Dato::Site::Client.new(
33
+ options[:token],
34
+ extra_headers: {
35
+ 'X-Reason' => 'migrate-slugs'
36
+ }
37
+ )
38
+
39
+ MigrateSlugs::Runner.new(client, options[:skip_id_prefix]).run
40
+ end
41
+ end
42
+ end
@@ -3,14 +3,14 @@ require 'active_support/core_ext/hash/keys'
3
3
  require 'toml'
4
4
 
5
5
  class Time
6
- def to_toml(path = "")
7
- utc.strftime("%Y-%m-%dT%H:%M:%SZ")
6
+ def to_toml(_path = '')
7
+ utc.strftime('%Y-%m-%dT%H:%M:%SZ')
8
8
  end
9
9
  end
10
10
 
11
11
  class Date
12
- def to_toml(path = "")
13
- strftime("%Y-%m-%d")
12
+ def to_toml(_path = '')
13
+ strftime('%Y-%m-%d')
14
14
  end
15
15
  end
16
16
 
@@ -16,7 +16,7 @@ module Dato
16
16
  end
17
17
 
18
18
  def run
19
- print "Fetching content from DatoCMS... "
19
+ print 'Fetching content from DatoCMS... '
20
20
 
21
21
  loader.load
22
22
 
@@ -26,7 +26,7 @@ module Dato
26
26
  },
27
27
  {
28
28
  file: 'config.json',
29
- loader: ->(content) { JSON.load(content) }
29
+ loader: ->(content) { JSON.parse(content) }
30
30
  }
31
31
  ].freeze
32
32
 
@@ -59,7 +59,7 @@ module Dato
59
59
  package_path = File.join(path, 'package.json')
60
60
  return unless File.exist?(package_path)
61
61
 
62
- package = JSON.load(File.read(package_path))
62
+ package = JSON.parse(File.read(package_path))
63
63
 
64
64
  deps = package.fetch('dependencies', {})
65
65
  dev_deps = package.fetch('devDependencies', {})
@@ -39,13 +39,12 @@ module Dato
39
39
  url: url
40
40
  }
41
41
  end
42
-
43
- private
44
-
42
+
43
+ private
44
+
45
45
  def default_host
46
46
  'dato-images.imgix.net'
47
47
  end
48
-
49
48
  end
50
49
  end
51
50
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Dato
3
+ module Local
4
+ module FieldType
5
+ class Slug
6
+ def self.parse(value, _repo)
7
+ value
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -24,14 +24,25 @@ module Dato
24
24
  other.is_a?(Item) && other.id == id
25
25
  end
26
26
 
27
- def slug(prefix_with_id: true)
28
- slug_field = fields.find { |f| f.api_key.to_sym == :slug }
27
+ def autogenerated_slug(options = {})
28
+ warning = [
29
+ 'Warning: the method `Item#autogenerated_slug` is deprecated:',
30
+ 'please add an explicit field of type `slug`',
31
+ "to the `#{item_type.api_key}` item type."
32
+ ]
33
+ puts warning.join(' ')
34
+
35
+ prefix_with_id = options.fetch(:prefix_with_id, true)
36
+
37
+ title_field = fields.find do |field|
38
+ field.field_type == 'string' &&
39
+ field.appeareance[:type] == 'title'
40
+ end
29
41
 
30
- return read_attribute(:slug, slug_field) if slug_field
31
42
  return item_type.api_key.humanize.parameterize if singleton?
32
- return id.to_s unless title_field_api_key
43
+ return id.to_s unless title_field
33
44
 
34
- title = send(title_field_api_key)
45
+ title = send(title_field.api_key)
35
46
  if title && prefix_with_id
36
47
  "#{id}-#{title.parameterize[0..50]}"
37
48
  elsif title
@@ -41,14 +52,6 @@ module Dato
41
52
  end
42
53
  end
43
54
 
44
- def title_field_api_key
45
- title_field = fields.find do |field|
46
- field.field_type == 'string' &&
47
- field.appeareance[:type] == 'title'
48
- end
49
- title_field && title_field.api_key
50
- end
51
-
52
55
  def singleton?
53
56
  item_type.singleton
54
57
  end
@@ -88,8 +91,6 @@ module Dato
88
91
  base = {
89
92
  id: id,
90
93
  item_type: item_type.api_key,
91
- slug: slug(prefix_with_id: false),
92
- slug_with_prefix: slug,
93
94
  updated_at: updated_at
94
95
  }
95
96
 
@@ -112,17 +113,24 @@ module Dato
112
113
  field_type = field.field_type
113
114
  type_klass_name = "::Dato::Local::FieldType::#{field_type.camelize}"
114
115
  type_klass = type_klass_name.safe_constantize
116
+ value = if field.localized
117
+ (entity.send(method) || {})[I18n.locale]
118
+ else
119
+ entity.send(method)
120
+ end
115
121
 
116
122
  if type_klass
117
- value = if field.localized
118
- (entity.send(method) || {})[I18n.locale]
119
- else
120
- entity.send(method)
121
- end
122
-
123
123
  value && type_klass.parse(value, @items_repo)
124
124
  else
125
- raise "Cannot convert field `#{method}` of type `#{field_type}`"
125
+ warning = [
126
+ "Warning: unrecognized field of type `#{field_type}`",
127
+ "for item `#{item_type.api_key}` and",
128
+ "field `#{method}`: returning a simple Hash instead.",
129
+ 'Please upgrade to the latest version of the `dato` gem!'
130
+ ]
131
+ puts warning.join(' ')
132
+
133
+ value
126
134
  end
127
135
  end
128
136
 
@@ -136,7 +144,7 @@ module Dato
136
144
  rescue NoMethodError
137
145
  message = []
138
146
  message << "Undefined method `#{method}`"
139
- message << 'Available fields for this DatoCMS item:'
147
+ message << "Available fields for a `#{item_type.api_key}` item:"
140
148
  message += fields.map do |f|
141
149
  "* .#{f.api_key}"
142
150
  end
@@ -34,7 +34,9 @@ module Dato
34
34
  items_per_page = 500
35
35
  base_response = client.request(:get, '/items', 'page[limit]' => 500)
36
36
 
37
- extra_pages = (base_response[:meta][:total_count] / items_per_page.to_f).ceil - 1
37
+ extra_pages = (
38
+ base_response[:meta][:total_count] / items_per_page.to_f
39
+ ).ceil - 1
38
40
 
39
41
  extra_pages.times do |page|
40
42
  base_response[:data] += client.request(
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+ require 'dato/json_api_deserializer'
3
+ require 'active_support/inflector/transliterate'
4
+
5
+ module Dato
6
+ module MigrateSlugs
7
+ class Runner
8
+ attr_reader :client, :skip_id_prefix
9
+
10
+ def initialize(client, skip_id_prefix)
11
+ @client = client
12
+ @skip_id_prefix = skip_id_prefix
13
+ end
14
+
15
+ def run
16
+ print 'Fetching site informations... '
17
+ title_fields
18
+ puts "\e[32m✓\e[0m"
19
+
20
+ title_fields.each do |title_field|
21
+ item_type = item_types.find do |i|
22
+ i['id'] == title_field['item_type']
23
+ end
24
+
25
+ print "Adding slug field to Item type `#{item_type['name']}`... "
26
+ add_slug_field(title_field)
27
+ puts "\e[32m✓\e[0m"
28
+
29
+ items = items_for(title_field['item_type'])
30
+ print "Generating slugs for #{items.count} items"
31
+
32
+ items.each do |item|
33
+ update_item(title_field, item)
34
+ print '.'
35
+ end
36
+ puts "\e[32m✓\e[0m"
37
+
38
+ puts
39
+ end
40
+ end
41
+
42
+ def simple_slugify(item, title, suffix)
43
+ if title
44
+ slug = title.parameterize[0..50].gsub(/(^\-|\-$)/, '')
45
+ skip_id_prefix ? "#{slug}#{suffix}" : "#{item['id']}-#{slug}#{suffix}"
46
+ end
47
+ end
48
+
49
+ def slugify(item, title, suffix)
50
+ if title.is_a?(Hash)
51
+ Hash[
52
+ title.map do |locale, value|
53
+ [locale, simple_slugify(item, value, suffix)]
54
+ end
55
+ ]
56
+ else
57
+ simple_slugify(item, title, suffix)
58
+ end
59
+ end
60
+
61
+ def update_item(title_field, item)
62
+ title = item[title_field['api_key']]
63
+ counter = 0
64
+
65
+ loop do
66
+ begin
67
+ slug = slugify(item, title, counter.zero? ? '' : "-#{counter}")
68
+ return client.items.update(item['id'], item.merge(slug: slug))
69
+ rescue ApiError => e
70
+ error = e.body['data'][0]
71
+
72
+ if error['id'] == 'INVALID_FIELD' &&
73
+ error['attributes']['details']['field'] == 'slug' &&
74
+ error['attributes']['details']['code'] == 'VALIDATION_UNIQUE'
75
+
76
+ counter += 1
77
+ else
78
+ raise e
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def items_for(item_type_id)
85
+ items_per_page = 500
86
+ base_response = client.request(
87
+ :get,
88
+ '/items',
89
+ 'page[limit]' => 500,
90
+ 'filter[type]' => item_type_id
91
+ )
92
+
93
+ extra_pages = (
94
+ base_response[:meta][:total_count] / items_per_page.to_f
95
+ ).ceil - 1
96
+
97
+ extra_pages.times do |page|
98
+ base_response[:data] += client.request(
99
+ :get,
100
+ '/items',
101
+ 'page[offset]' => items_per_page * (page + 1),
102
+ 'page[limit]' => items_per_page
103
+ )[:data]
104
+ end
105
+
106
+ JsonApiDeserializer.new.deserialize(base_response)
107
+ end
108
+
109
+ def add_slug_field(field)
110
+ validators = {
111
+ unique: {}
112
+ }
113
+
114
+ validators[:required] = {} if field['validators']['required']
115
+
116
+ slug_field = client.fields.create(
117
+ field['item_type'],
118
+ field_type: 'slug',
119
+ appeareance: { title_field_id: field['id'] },
120
+ validators: validators,
121
+ position: 99,
122
+ api_key: 'slug',
123
+ label: 'Slug',
124
+ hint: '',
125
+ localized: field['localized']
126
+ )
127
+
128
+ client.fields.update(
129
+ slug_field['id'],
130
+ position: field['position'] + 1
131
+ )
132
+ end
133
+
134
+ def title_fields
135
+ @title_fields ||= item_types.map do |item_type|
136
+ fields = client.fields.all(item_type['id'])
137
+
138
+ any_slug_present = fields.any? do |f|
139
+ f['field_type'] == 'slug' || f['api_key'] == 'slug'
140
+ end
141
+
142
+ next if any_slug_present
143
+ fields.find do |field|
144
+ field['field_type'] == 'string' &&
145
+ field['appeareance']['type'] == 'title'
146
+ end
147
+ end.compact
148
+ end
149
+
150
+ def item_types
151
+ @item_types ||= client.item_types.all
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Dato
3
- VERSION = '0.1.31'
3
+ VERSION = '0.2.4'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dato
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.31
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefano Verna
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-10-28 00:00:00.000000000 Z
11
+ date: 2016-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -338,7 +338,6 @@ files:
338
338
  - Rakefile
339
339
  - bin/console
340
340
  - bin/setup
341
- - dato.config.rb
342
341
  - dato.gemspec
343
342
  - docs/account-api-client.md
344
343
  - docs/dato-cli.md
@@ -350,7 +349,7 @@ files:
350
349
  - lib/dato/account/repo/base.rb
351
350
  - lib/dato/account/repo/site.rb
352
351
  - lib/dato/api_error.rb
353
- - lib/dato/dump/cli.rb
352
+ - lib/dato/cli.rb
354
353
  - lib/dato/dump/dsl/add_to_data_file.rb
355
354
  - lib/dato/dump/dsl/create_data_file.rb
356
355
  - lib/dato/dump/dsl/create_post.rb
@@ -381,6 +380,7 @@ files:
381
380
  - lib/dato/local/field_type/link.rb
382
381
  - lib/dato/local/field_type/links.rb
383
382
  - lib/dato/local/field_type/seo.rb
383
+ - lib/dato/local/field_type/slug.rb
384
384
  - lib/dato/local/field_type/string.rb
385
385
  - lib/dato/local/field_type/text.rb
386
386
  - lib/dato/local/field_type/video.rb
@@ -389,6 +389,7 @@ files:
389
389
  - lib/dato/local/json_api_entity.rb
390
390
  - lib/dato/local/loader.rb
391
391
  - lib/dato/local/site.rb
392
+ - lib/dato/migrate_slugs/runner.rb
392
393
  - lib/dato/site/client.rb
393
394
  - lib/dato/site/repo/base.rb
394
395
  - lib/dato/site/repo/field.rb
@@ -421,7 +422,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
421
422
  version: '0'
422
423
  requirements: []
423
424
  rubyforge_project:
424
- rubygems_version: 2.2.0
425
+ rubygems_version: 2.4.5
425
426
  signing_key:
426
427
  specification_version: 4
427
428
  summary: Ruby client for DatoCMS API
@@ -1 +0,0 @@
1
- puts dato.blog_posts.first.title
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'thor'
3
- require 'dato/dump/runner'
4
-
5
- module Dato
6
- module Dump
7
- class Cli < Thor
8
- package_name 'DatoCMS'
9
-
10
- desc 'dump', 'dumps DatoCMS contents into local files'
11
- option :config, default: 'dato.config.rb'
12
- option :token, default: ENV['DATO_API_TOKEN'], required: true
13
- def dump
14
- config_file = File.expand_path(options[:config])
15
-
16
- client = Dato::Site::Client.new(
17
- options[:token],
18
- extra_headers: {
19
- 'X-Reason' => 'dump',
20
- 'X-SSG' => SsgDetector.new(Dir.pwd).detect
21
- }
22
- )
23
-
24
- Runner.new(config_file, client).run
25
- end
26
- end
27
- end
28
- end