agave-client 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +14 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/CONTRIBUTORS.md +34 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +29 -0
- data/README.md +41 -0
- data/Rakefile +7 -0
- data/agave-client.gemspec +63 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/agave +10 -0
- data/lib/agave.rb +11 -0
- data/lib/agave/api_client.rb +97 -0
- data/lib/agave/api_error.rb +23 -0
- data/lib/agave/cli.rb +93 -0
- data/lib/agave/dump/dsl/add_to_data_file.rb +14 -0
- data/lib/agave/dump/dsl/create_data_file.rb +14 -0
- data/lib/agave/dump/dsl/create_post.rb +33 -0
- data/lib/agave/dump/dsl/directory.rb +30 -0
- data/lib/agave/dump/dsl/root.rb +37 -0
- data/lib/agave/dump/format.rb +28 -0
- data/lib/agave/dump/format/json.rb +18 -0
- data/lib/agave/dump/format/toml.rb +31 -0
- data/lib/agave/dump/format/yaml.rb +55 -0
- data/lib/agave/dump/operation/add_to_data_file.rb +41 -0
- data/lib/agave/dump/operation/create_data_file.rb +28 -0
- data/lib/agave/dump/operation/create_post.rb +34 -0
- data/lib/agave/dump/operation/directory.rb +34 -0
- data/lib/agave/dump/operation/root.rb +27 -0
- data/lib/agave/dump/runner.rb +47 -0
- data/lib/agave/dump/ssg_detector.rb +36 -0
- data/lib/agave/json_api_deserializer.rb +38 -0
- data/lib/agave/json_api_serializer.rb +145 -0
- data/lib/agave/local/entities_repo.rb +46 -0
- data/lib/agave/local/field_type/boolean.rb +12 -0
- data/lib/agave/local/field_type/color.rb +63 -0
- data/lib/agave/local/field_type/date.rb +12 -0
- data/lib/agave/local/field_type/date_time.rb +12 -0
- data/lib/agave/local/field_type/file.rb +65 -0
- data/lib/agave/local/field_type/float.rb +12 -0
- data/lib/agave/local/field_type/gallery.rb +23 -0
- data/lib/agave/local/field_type/global_seo.rb +56 -0
- data/lib/agave/local/field_type/image.rb +82 -0
- data/lib/agave/local/field_type/integer.rb +12 -0
- data/lib/agave/local/field_type/json.rb +13 -0
- data/lib/agave/local/field_type/lat_lon.rb +30 -0
- data/lib/agave/local/field_type/link.rb +12 -0
- data/lib/agave/local/field_type/links.rb +21 -0
- data/lib/agave/local/field_type/rich_text.rb +21 -0
- data/lib/agave/local/field_type/seo.rb +33 -0
- data/lib/agave/local/field_type/slug.rb +12 -0
- data/lib/agave/local/field_type/string.rb +12 -0
- data/lib/agave/local/field_type/text.rb +12 -0
- data/lib/agave/local/field_type/theme.rb +43 -0
- data/lib/agave/local/field_type/video.rb +77 -0
- data/lib/agave/local/item.rb +179 -0
- data/lib/agave/local/items_repo.rb +210 -0
- data/lib/agave/local/json_api_entity.rb +78 -0
- data/lib/agave/local/loader.rb +60 -0
- data/lib/agave/local/site.rb +73 -0
- data/lib/agave/paginator.rb +33 -0
- data/lib/agave/repo.rb +93 -0
- data/lib/agave/site/client.rb +24 -0
- data/lib/agave/upload/file.rb +92 -0
- data/lib/agave/upload/image.rb +8 -0
- data/lib/agave/utils/favicon_tags_builder.rb +85 -0
- data/lib/agave/utils/locale_value.rb +15 -0
- data/lib/agave/utils/meta_tags/article_modified_time.rb +15 -0
- data/lib/agave/utils/meta_tags/article_publisher.rb +18 -0
- data/lib/agave/utils/meta_tags/base.rb +55 -0
- data/lib/agave/utils/meta_tags/description.rb +24 -0
- data/lib/agave/utils/meta_tags/image.rb +35 -0
- data/lib/agave/utils/meta_tags/og_locale.rb +15 -0
- data/lib/agave/utils/meta_tags/og_site_name.rb +18 -0
- data/lib/agave/utils/meta_tags/og_type.rb +18 -0
- data/lib/agave/utils/meta_tags/robots.rb +14 -0
- data/lib/agave/utils/meta_tags/title.rb +46 -0
- data/lib/agave/utils/meta_tags/twitter_card.rb +14 -0
- data/lib/agave/utils/meta_tags/twitter_site.rb +18 -0
- data/lib/agave/utils/seo_tags_builder.rb +45 -0
- data/lib/agave/version.rb +4 -0
- data/lib/agave/watch/site_change_watcher.rb +37 -0
- metadata +504 -0
data/lib/agave/cli.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'thor'
|
3
|
+
require 'agave/dump/runner'
|
4
|
+
require 'agave/dump/ssg_detector'
|
5
|
+
require 'agave/watch/site_change_watcher'
|
6
|
+
require 'listen'
|
7
|
+
require 'thread'
|
8
|
+
|
9
|
+
module Agave
|
10
|
+
class Cli < Thor
|
11
|
+
package_name 'AgaveCMS'
|
12
|
+
|
13
|
+
desc 'dump', 'dumps AgaveCMS content into local files'
|
14
|
+
option :config, default: 'agave.config.rb'
|
15
|
+
option :token, default: ENV['AGAVE_API_TOKEN'], required: true
|
16
|
+
option :preview, default: false, type: :boolean
|
17
|
+
option :watch, default: false, type: :boolean
|
18
|
+
def dump
|
19
|
+
if !ENV['AGAVE_API_TOKEN']
|
20
|
+
say 'Site token is not specified! Please configure AGAVE_API_TOKEN.'
|
21
|
+
exit 0
|
22
|
+
end
|
23
|
+
|
24
|
+
config_file = File.expand_path(options[:config])
|
25
|
+
watch_mode = options[:watch]
|
26
|
+
preview_mode = options[:preview]
|
27
|
+
|
28
|
+
client = Agave::Site::Client.new(
|
29
|
+
extra_headers: {
|
30
|
+
'X-Reason' => 'dump',
|
31
|
+
'X-SSG' => Dump::SsgDetector.new(Dir.pwd).detect
|
32
|
+
}
|
33
|
+
)
|
34
|
+
|
35
|
+
if watch_mode
|
36
|
+
site_id = client.request(:get, '/api/site')['data']['id']
|
37
|
+
|
38
|
+
semaphore = Mutex.new
|
39
|
+
|
40
|
+
thread_safe_dump(semaphore, config_file, client, preview_mode)
|
41
|
+
|
42
|
+
Agave::Watch::SiteChangeWatcher.new(site_id).connect do
|
43
|
+
thread_safe_dump(semaphore, config_file, client, preview_mode)
|
44
|
+
end
|
45
|
+
|
46
|
+
watch_config_file(config_file) do
|
47
|
+
thread_safe_dump(semaphore, config_file, client, preview_mode)
|
48
|
+
end
|
49
|
+
|
50
|
+
sleep
|
51
|
+
else
|
52
|
+
Dump::Runner.new(config_file, client, preview_mode).run
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'check', 'checks the presence of a AgaveCMS token'
|
57
|
+
def check
|
58
|
+
exit 0 if ENV['AGAVE_API_TOKEN']
|
59
|
+
|
60
|
+
say 'Site token is not specified!'
|
61
|
+
token = ask "Please paste your AgaveCMS site read-only API token:\n>"
|
62
|
+
|
63
|
+
if !token || token.empty?
|
64
|
+
puts 'Missing token'
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
File.open('.env', 'a') do |file|
|
69
|
+
file.puts "AGAVE_API_TOKEN=#{token}"
|
70
|
+
end
|
71
|
+
|
72
|
+
say 'Token added to .env file.'
|
73
|
+
|
74
|
+
exit 0
|
75
|
+
end
|
76
|
+
|
77
|
+
no_tasks do
|
78
|
+
def watch_config_file(config_file, &block)
|
79
|
+
Listen.to(
|
80
|
+
File.dirname(config_file),
|
81
|
+
only: /#{Regexp.quote(File.basename(config_file))}/,
|
82
|
+
&block
|
83
|
+
).start
|
84
|
+
end
|
85
|
+
|
86
|
+
def thread_safe_dump(semaphore, config_file, client, preview_mode)
|
87
|
+
semaphore.synchronize do
|
88
|
+
Dump::Runner.new(config_file, client, preview_mode).run
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/operation/add_to_data_file'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Dsl
|
7
|
+
module AddToDataFile
|
8
|
+
def add_to_data_file(*args)
|
9
|
+
operations.add Operation::AddToDataFile.new(operations, *args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/operation/create_data_file'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Dsl
|
7
|
+
module CreateDataFile
|
8
|
+
def create_data_file(*args)
|
9
|
+
operations.add Operation::CreateDataFile.new(operations, *args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/operation/create_post'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Dsl
|
7
|
+
class DataFile
|
8
|
+
def initialize(operation, &block)
|
9
|
+
@operation = operation
|
10
|
+
instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def frontmatter(format, value)
|
14
|
+
@operation.frontmatter_format = format
|
15
|
+
@operation.frontmatter_value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def content(value)
|
19
|
+
@operation.content = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module CreatePost
|
24
|
+
def create_post(path, &block)
|
25
|
+
operation = Operation::CreatePost.new(operations, path)
|
26
|
+
DataFile.new(operation, &block)
|
27
|
+
|
28
|
+
operations.add operation
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/dsl/create_post'
|
3
|
+
require 'agave/dump/dsl/create_data_file'
|
4
|
+
require 'agave/dump/dsl/add_to_data_file'
|
5
|
+
|
6
|
+
module Agave
|
7
|
+
module Dump
|
8
|
+
module Dsl
|
9
|
+
class Directory
|
10
|
+
include Dsl::CreateDataFile
|
11
|
+
include Dsl::CreatePost
|
12
|
+
include Dsl::AddToDataFile
|
13
|
+
|
14
|
+
attr_reader :agave, :operations
|
15
|
+
|
16
|
+
def initialize(agave, operations, &block)
|
17
|
+
@agave = agave
|
18
|
+
@operations = operations
|
19
|
+
@self_before_instance_eval = eval 'self', block.binding
|
20
|
+
|
21
|
+
instance_eval(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args, &block)
|
25
|
+
@self_before_instance_eval.send method, *args, &block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/dsl/directory'
|
3
|
+
require 'agave/dump/dsl/create_post'
|
4
|
+
require 'agave/dump/dsl/create_data_file'
|
5
|
+
require 'agave/dump/dsl/add_to_data_file'
|
6
|
+
|
7
|
+
require 'agave/dump/operation/directory'
|
8
|
+
|
9
|
+
module Agave
|
10
|
+
module Dump
|
11
|
+
module Dsl
|
12
|
+
class Root
|
13
|
+
include Dsl::CreateDataFile
|
14
|
+
include Dsl::CreatePost
|
15
|
+
include Dsl::AddToDataFile
|
16
|
+
|
17
|
+
attr_reader :agave, :operations
|
18
|
+
|
19
|
+
def initialize(config_code, agave, operations)
|
20
|
+
@agave = agave
|
21
|
+
@operations = operations
|
22
|
+
|
23
|
+
# rubocop:disable Lint/Eval
|
24
|
+
eval(config_code)
|
25
|
+
# rubocop:enable Lint/Eval
|
26
|
+
end
|
27
|
+
|
28
|
+
def directory(path, &block)
|
29
|
+
operation = Operation::Directory.new(operations, path)
|
30
|
+
operations.add operation
|
31
|
+
|
32
|
+
Directory.new(agave, operation, &block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/format/toml'
|
3
|
+
require 'agave/dump/format/yaml'
|
4
|
+
|
5
|
+
module Agave
|
6
|
+
module Dump
|
7
|
+
module Format
|
8
|
+
def self.dump(format, value)
|
9
|
+
converter_for(format).dump(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.frontmatter_dump(format, value)
|
13
|
+
converter_for(format).frontmatter_dump(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.converter_for(format)
|
17
|
+
case format.to_sym
|
18
|
+
when :toml
|
19
|
+
Format::Toml
|
20
|
+
when :yaml, :yml
|
21
|
+
Format::Yaml
|
22
|
+
when :json
|
23
|
+
Format::Json
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Format
|
7
|
+
module Json
|
8
|
+
def self.dump(value)
|
9
|
+
JSON.dump(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.frontmatter_dump(value)
|
13
|
+
"#{dump(value)}\n"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'toml'
|
4
|
+
|
5
|
+
class Time
|
6
|
+
def to_toml(_path = '')
|
7
|
+
utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Date
|
12
|
+
def to_toml(_path = '')
|
13
|
+
strftime('%Y-%m-%d')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Agave
|
18
|
+
module Dump
|
19
|
+
module Format
|
20
|
+
module Toml
|
21
|
+
def self.dump(value)
|
22
|
+
TOML::Generator.new(value).body
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.frontmatter_dump(value)
|
26
|
+
"+++\n#{dump(value)}+++"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/core_ext/hash/keys'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'agave/local/item'
|
6
|
+
|
7
|
+
class Array
|
8
|
+
def deep_stringify_keys
|
9
|
+
each_with_object([]) do |value, accum|
|
10
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
11
|
+
new_val = value.deep_stringify_keys
|
12
|
+
accum.push new_val
|
13
|
+
else
|
14
|
+
accum.push value
|
15
|
+
end
|
16
|
+
accum
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Agave
|
22
|
+
module Dump
|
23
|
+
module Format
|
24
|
+
module Yaml
|
25
|
+
def self.deep_hashify_items(value)
|
26
|
+
case value
|
27
|
+
when Array
|
28
|
+
value.map { |v| deep_hashify_items(v) }
|
29
|
+
when Hash
|
30
|
+
value.each_with_object({}) do |(k, v), acc|
|
31
|
+
acc[k] = deep_hashify_items(v)
|
32
|
+
end
|
33
|
+
when ::Agave::Local::Item
|
34
|
+
value.to_hash
|
35
|
+
else
|
36
|
+
if value.respond_to?(:to_hash)
|
37
|
+
value.to_hash
|
38
|
+
else
|
39
|
+
value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.dump(value)
|
45
|
+
plain = deep_hashify_items(value)
|
46
|
+
YAML.dump(plain.deep_stringify_keys).chomp.gsub(/^\-+\n/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.frontmatter_dump(value)
|
50
|
+
"---\n#{dump(value)}\n---"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'agave/dump/format'
|
3
|
+
|
4
|
+
module Agave
|
5
|
+
module Dump
|
6
|
+
module Operation
|
7
|
+
class AddToDataFile
|
8
|
+
attr_reader :context, :path, :format, :value
|
9
|
+
|
10
|
+
def initialize(context, path, format, value)
|
11
|
+
@context = context
|
12
|
+
@path = path
|
13
|
+
@format = format
|
14
|
+
@value = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform
|
18
|
+
complete_path = File.join(context.path, path)
|
19
|
+
FileUtils.mkdir_p(File.dirname(complete_path))
|
20
|
+
|
21
|
+
content_to_add = Format.dump(format, value)
|
22
|
+
|
23
|
+
old_content = if File.exist? complete_path
|
24
|
+
::File.read(complete_path)
|
25
|
+
else
|
26
|
+
''
|
27
|
+
end
|
28
|
+
|
29
|
+
new_content = old_content.sub(
|
30
|
+
/\n*(#\s*agavecms:start.*#\s*agavecms:end|\Z)/m,
|
31
|
+
"\n\n# agavecms:start\n#{content_to_add}\n# agavecms:end"
|
32
|
+
)
|
33
|
+
|
34
|
+
File.open(complete_path, 'w') do |f|
|
35
|
+
f.write new_content
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
require 'agave/dump/format'
|
4
|
+
|
5
|
+
module Agave
|
6
|
+
module Dump
|
7
|
+
module Operation
|
8
|
+
class CreateDataFile
|
9
|
+
attr_reader :context, :path, :format, :value
|
10
|
+
|
11
|
+
def initialize(context, path, format, value)
|
12
|
+
@context = context
|
13
|
+
@path = path
|
14
|
+
@format = format
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def perform
|
19
|
+
FileUtils.mkdir_p(File.dirname(path))
|
20
|
+
|
21
|
+
File.open(File.join(context.path, path), 'w') do |file|
|
22
|
+
file.write Format.dump(format, value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
require 'agave/dump/format'
|
4
|
+
|
5
|
+
module Agave
|
6
|
+
module Dump
|
7
|
+
module Operation
|
8
|
+
class CreatePost
|
9
|
+
attr_reader :context, :path
|
10
|
+
|
11
|
+
attr_accessor :frontmatter_format, :frontmatter_value
|
12
|
+
attr_accessor :content
|
13
|
+
|
14
|
+
def initialize(context, path)
|
15
|
+
@context = context
|
16
|
+
@path = path
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform
|
20
|
+
FileUtils.mkdir_p(File.dirname(path))
|
21
|
+
|
22
|
+
File.open(File.join(context.path, path), 'w') do |file|
|
23
|
+
file.write Format.frontmatter_dump(
|
24
|
+
frontmatter_format,
|
25
|
+
frontmatter_value
|
26
|
+
)
|
27
|
+
file.write "\n\n"
|
28
|
+
file.write content
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|