agave-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +14 -0
  4. data/CODE_OF_CONDUCT.md +49 -0
  5. data/CONTRIBUTORS.md +34 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE.txt +29 -0
  8. data/README.md +41 -0
  9. data/Rakefile +7 -0
  10. data/agave-client.gemspec +63 -0
  11. data/bin/console +15 -0
  12. data/bin/setup +8 -0
  13. data/exe/agave +10 -0
  14. data/lib/agave.rb +11 -0
  15. data/lib/agave/api_client.rb +97 -0
  16. data/lib/agave/api_error.rb +23 -0
  17. data/lib/agave/cli.rb +93 -0
  18. data/lib/agave/dump/dsl/add_to_data_file.rb +14 -0
  19. data/lib/agave/dump/dsl/create_data_file.rb +14 -0
  20. data/lib/agave/dump/dsl/create_post.rb +33 -0
  21. data/lib/agave/dump/dsl/directory.rb +30 -0
  22. data/lib/agave/dump/dsl/root.rb +37 -0
  23. data/lib/agave/dump/format.rb +28 -0
  24. data/lib/agave/dump/format/json.rb +18 -0
  25. data/lib/agave/dump/format/toml.rb +31 -0
  26. data/lib/agave/dump/format/yaml.rb +55 -0
  27. data/lib/agave/dump/operation/add_to_data_file.rb +41 -0
  28. data/lib/agave/dump/operation/create_data_file.rb +28 -0
  29. data/lib/agave/dump/operation/create_post.rb +34 -0
  30. data/lib/agave/dump/operation/directory.rb +34 -0
  31. data/lib/agave/dump/operation/root.rb +27 -0
  32. data/lib/agave/dump/runner.rb +47 -0
  33. data/lib/agave/dump/ssg_detector.rb +36 -0
  34. data/lib/agave/json_api_deserializer.rb +38 -0
  35. data/lib/agave/json_api_serializer.rb +145 -0
  36. data/lib/agave/local/entities_repo.rb +46 -0
  37. data/lib/agave/local/field_type/boolean.rb +12 -0
  38. data/lib/agave/local/field_type/color.rb +63 -0
  39. data/lib/agave/local/field_type/date.rb +12 -0
  40. data/lib/agave/local/field_type/date_time.rb +12 -0
  41. data/lib/agave/local/field_type/file.rb +65 -0
  42. data/lib/agave/local/field_type/float.rb +12 -0
  43. data/lib/agave/local/field_type/gallery.rb +23 -0
  44. data/lib/agave/local/field_type/global_seo.rb +56 -0
  45. data/lib/agave/local/field_type/image.rb +82 -0
  46. data/lib/agave/local/field_type/integer.rb +12 -0
  47. data/lib/agave/local/field_type/json.rb +13 -0
  48. data/lib/agave/local/field_type/lat_lon.rb +30 -0
  49. data/lib/agave/local/field_type/link.rb +12 -0
  50. data/lib/agave/local/field_type/links.rb +21 -0
  51. data/lib/agave/local/field_type/rich_text.rb +21 -0
  52. data/lib/agave/local/field_type/seo.rb +33 -0
  53. data/lib/agave/local/field_type/slug.rb +12 -0
  54. data/lib/agave/local/field_type/string.rb +12 -0
  55. data/lib/agave/local/field_type/text.rb +12 -0
  56. data/lib/agave/local/field_type/theme.rb +43 -0
  57. data/lib/agave/local/field_type/video.rb +77 -0
  58. data/lib/agave/local/item.rb +179 -0
  59. data/lib/agave/local/items_repo.rb +210 -0
  60. data/lib/agave/local/json_api_entity.rb +78 -0
  61. data/lib/agave/local/loader.rb +60 -0
  62. data/lib/agave/local/site.rb +73 -0
  63. data/lib/agave/paginator.rb +33 -0
  64. data/lib/agave/repo.rb +93 -0
  65. data/lib/agave/site/client.rb +24 -0
  66. data/lib/agave/upload/file.rb +92 -0
  67. data/lib/agave/upload/image.rb +8 -0
  68. data/lib/agave/utils/favicon_tags_builder.rb +85 -0
  69. data/lib/agave/utils/locale_value.rb +15 -0
  70. data/lib/agave/utils/meta_tags/article_modified_time.rb +15 -0
  71. data/lib/agave/utils/meta_tags/article_publisher.rb +18 -0
  72. data/lib/agave/utils/meta_tags/base.rb +55 -0
  73. data/lib/agave/utils/meta_tags/description.rb +24 -0
  74. data/lib/agave/utils/meta_tags/image.rb +35 -0
  75. data/lib/agave/utils/meta_tags/og_locale.rb +15 -0
  76. data/lib/agave/utils/meta_tags/og_site_name.rb +18 -0
  77. data/lib/agave/utils/meta_tags/og_type.rb +18 -0
  78. data/lib/agave/utils/meta_tags/robots.rb +14 -0
  79. data/lib/agave/utils/meta_tags/title.rb +46 -0
  80. data/lib/agave/utils/meta_tags/twitter_card.rb +14 -0
  81. data/lib/agave/utils/meta_tags/twitter_site.rb +18 -0
  82. data/lib/agave/utils/seo_tags_builder.rb +45 -0
  83. data/lib/agave/version.rb +4 -0
  84. data/lib/agave/watch/site_change_watcher.rb +37 -0
  85. metadata +504 -0
@@ -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