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