dato 0.1.31 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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