archival 0.0.2 → 0.0.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
  SHA256:
3
- metadata.gz: 9c657b6a9eef44687dc75a8c85349309433c224fc7a0871890c4c50aa955745f
4
- data.tar.gz: a857f9490e10cf5aa9e49e9df1d5adbb40908dd1731d00a81b41361fb4bbcc4c
3
+ metadata.gz: 71c5bb3b87870dd8146038f624251b7a9ef351a7d69f8da16c214c91ea740cfb
4
+ data.tar.gz: 6978ae618a2e5312178e5c0d1eb7174c13f3ef0512d0ef31d83f5012e0bd9712
5
5
  SHA512:
6
- metadata.gz: 5bcf4e0b7678dc60aa1769a1afa586cbd3569a36b7d863c5fb83a7fe45e4249f6a176a411e17815c4ce41d0dc95d233f0fdaf7af640dfff81eba4851f54af387
7
- data.tar.gz: 9738d2bc0bfbad7612c9ff5e225e5787691ef736824ff4a546e25bab3855c70af7cffeae2610eb486551dc8fb1f44f885223664a48e41d9bd4f0260eb45615dd
6
+ metadata.gz: df27c765403ac93aa5bd762ca7693b589aa86a0a80e31a93226fce2a0a756f2167417036cd76e4c96fda55071f3af92884628ac56f2c42bac8995a799432c32d
7
+ data.tar.gz: 4e785cf1c9bc97a152a737bbc03da981a65b8ea68e0b98fe6fc71196da82fbe928170c983f4dd11a857a9b51c4a1e854f268c42137c184ce15312e4dc9f5cc72
data/.rubocop.yml CHANGED
@@ -1,14 +1,17 @@
1
+ inherit_mode:
2
+ merge:
3
+ - Exclude
4
+
1
5
  AllCops:
2
6
  NewCops: enable
3
7
  SuggestExtensions: false
4
8
  Exclude:
5
- - bin/bundle
9
+ - bin/*
6
10
 
7
11
  Layout/LineLength:
8
12
  Max: 80
9
13
  Exclude:
10
14
  - archival.gemspec
11
- - bin/*
12
15
  - spec/spec_helper.rb
13
16
 
14
17
  Layout/TrailingWhitespace:
@@ -29,6 +32,18 @@ Metrics/BlockLength:
29
32
  Metrics/ClassLength:
30
33
  Max: 150
31
34
 
35
+ Metrics/CyclomaticComplexity:
36
+ Max: 10
37
+ Exclude:
38
+ # This file does a lot of defaulting. It's easy to read.
39
+ - lib/archival/config.rb
40
+
41
+ Metrics/PerceivedComplexity:
42
+ Max: 10
43
+ Exclude:
44
+ # This file does a lot of defaulting. It's easy to read.
45
+ - lib/archival/config.rb
46
+
32
47
  Lint/ImplicitStringConcatenation:
33
48
  Exclude:
34
49
  - spec/tags/layout_spec.rb
data/Rakefile CHANGED
@@ -10,3 +10,4 @@ RuboCop::RakeTask.new
10
10
 
11
11
  task default: :spec
12
12
  task lint: :rubocop
13
+ task 'lint:fix': 'rubocop:auto_correct'
data/archival.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'archival'
5
- s.version = '0.0.2'
5
+ s.version = '0.0.4'
6
6
  s.summary = 'An incredibly simple CMS for durable websites'
7
7
  s.description = 'https://jesseditson.com/the-simplest-cms-part-1'
8
8
  s.authors = ['Jesse Ditson']
data/bin/archival CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('archival', 'archival')
29
+ load Gem.bin_path("archival", "archival")
data/bin/bundle CHANGED
@@ -8,46 +8,46 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'rubygems'
11
+ require "rubygems"
12
12
 
13
13
  m = Module.new do
14
14
  module_function
15
15
 
16
16
  def invoked_as_script?
17
- File.expand_path($PROGRAM_NAME) == File.expand_path(__FILE__)
17
+ File.expand_path($0) == File.expand_path(__FILE__)
18
18
  end
19
19
 
20
20
  def env_var_version
21
- ENV['BUNDLER_VERSION']
21
+ ENV["BUNDLER_VERSION"]
22
22
  end
23
23
 
24
24
  def cli_arg_version
25
25
  return unless invoked_as_script? # don't want to hijack other binstubs
26
- return unless 'update'.start_with?(ARGV.first || ' ') # must be running `bundle update`
27
-
26
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
28
27
  bundler_version = nil
29
28
  update_index = nil
30
29
  ARGV.each_with_index do |a, i|
31
- bundler_version = a if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
30
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31
+ bundler_version = a
32
+ end
32
33
  next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
33
-
34
- bundler_version = Regexp.last_match(1)
34
+ bundler_version = $1
35
35
  update_index = i
36
36
  end
37
37
  bundler_version
38
38
  end
39
39
 
40
40
  def gemfile
41
- gemfile = ENV['BUNDLE_GEMFILE']
41
+ gemfile = ENV["BUNDLE_GEMFILE"]
42
42
  return gemfile if gemfile && !gemfile.empty?
43
43
 
44
- File.expand_path('../Gemfile', __dir__)
44
+ File.expand_path("../../Gemfile", __FILE__)
45
45
  end
46
46
 
47
47
  def lockfile
48
48
  lockfile =
49
49
  case File.basename(gemfile)
50
- when 'gems.rb' then gemfile.sub(/\.rb$/, gemfile)
50
+ when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51
51
  else "#{gemfile}.lock"
52
52
  end
53
53
  File.expand_path(lockfile)
@@ -55,17 +55,15 @@ m = Module.new do
55
55
 
56
56
  def lockfile_version
57
57
  return unless File.file?(lockfile)
58
-
59
58
  lockfile_contents = File.read(lockfile)
60
59
  return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
61
-
62
60
  Regexp.last_match(1)
63
61
  end
64
62
 
65
63
  def bundler_requirement
66
64
  @bundler_requirement ||=
67
65
  env_var_version || cli_arg_version ||
68
- bundler_requirement_for(lockfile_version)
66
+ bundler_requirement_for(lockfile_version)
69
67
  end
70
68
 
71
69
  def bundler_requirement_for(version)
@@ -75,32 +73,28 @@ m = Module.new do
75
73
 
76
74
  requirement = bundler_gem_version.approximate_recommendation
77
75
 
78
- return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new('2.7.0')
76
+ return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
79
77
 
80
- requirement += '.a' if bundler_gem_version.prerelease?
78
+ requirement += ".a" if bundler_gem_version.prerelease?
81
79
 
82
80
  requirement
83
81
  end
84
82
 
85
83
  def load_bundler!
86
- ENV['BUNDLE_GEMFILE'] ||= gemfile
84
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
87
85
 
88
86
  activate_bundler
89
87
  end
90
88
 
91
89
  def activate_bundler
92
90
  gem_error = activation_error_handling do
93
- gem 'bundler', bundler_requirement
91
+ gem "bundler", bundler_requirement
94
92
  end
95
93
  return if gem_error.nil?
96
-
97
94
  require_error = activation_error_handling do
98
- require 'bundler/version'
99
- end
100
- if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
101
- return
95
+ require "bundler/version"
102
96
  end
103
-
97
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
104
98
  warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
105
99
  exit 42
106
100
  end
@@ -115,4 +109,6 @@ end
115
109
 
116
110
  m.load_bundler!
117
111
 
118
- load Gem.bin_path('bundler', 'bundle') if m.invoked_as_script?
112
+ if m.invoked_as_script?
113
+ load Gem.bin_path("bundler", "bundle")
114
+ end
data/bin/htmldiff CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('diff-lcs', 'htmldiff')
29
+ load Gem.bin_path("diff-lcs", "htmldiff")
data/bin/ldiff CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('diff-lcs', 'ldiff')
29
+ load Gem.bin_path("diff-lcs", "ldiff")
data/bin/listen CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('listen', 'listen')
29
+ load Gem.bin_path("listen", "listen")
data/bin/rake CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('rake', 'rake')
29
+ load Gem.bin_path("rake", "rake")
data/bin/rspec CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('rspec-core', 'rspec')
29
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/rubocop CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('rubocop', 'rubocop')
29
+ load Gem.bin_path("rubocop", "rubocop")
data/bin/ruby-parse CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('parser', 'ruby-parse')
29
+ load Gem.bin_path("parser", "ruby-parse")
data/bin/ruby-rewrite CHANGED
@@ -8,16 +8,14 @@
8
8
  # this file is here to facilitate running it.
9
9
  #
10
10
 
11
- require 'pathname'
12
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
- Pathname.new(__FILE__).realpath)
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
14
 
15
- bundle_binstub = File.expand_path('bundle',
16
- __dir__)
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
17
16
 
18
17
  if File.file?(bundle_binstub)
19
- if File.read(bundle_binstub,
20
- 300) =~ /This file was generated by Bundler/
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
19
  load(bundle_binstub)
22
20
  else
23
21
  abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
@@ -25,7 +23,7 @@ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this
25
23
  end
26
24
  end
27
25
 
28
- require 'rubygems'
29
- require 'bundler/setup'
26
+ require "rubygems"
27
+ require "bundler/setup"
30
28
 
31
- load Gem.bin_path('parser', 'ruby-rewrite')
29
+ load Gem.bin_path("parser", "ruby-rewrite")
data/exe/archival CHANGED
@@ -19,17 +19,13 @@ build_dir = Dir.pwd
19
19
 
20
20
  case command
21
21
  when 'build'
22
- builder = Archival::Builder.new('root' => build_dir)
23
- builder.write_all
24
- when 'run'
25
- Archival.listen(build_dir)
26
- begin
27
- sleep
28
- rescue Interrupt
29
- # Don't print a stack when a user interrupts, as this is the right way to
30
- # stop the development server.
31
- puts ''
22
+ Archival::Logger.benchmark('built') do
23
+ config = Archival::Config.new('root' => build_dir)
24
+ builder = Archival::Builder.new(config)
25
+ builder.write_all
32
26
  end
27
+ when 'run'
28
+ Archival.listen('root' => build_dir)
33
29
  else
34
30
  # print help
35
31
  puts 'archival [command]'
@@ -0,0 +1,91 @@
1
+ /**
2
+ * When running archival in development mode (archival run), this file is
3
+ * injected into all pages, and is responsible for reloading the page when the
4
+ * source has changed.
5
+ */
6
+
7
+ (function () {
8
+ const remotePort = $PORT;
9
+ const CONNECTING_COLOR = "#bd270d";
10
+ const CONNECTED_COLOR = "#19bd0d";
11
+ const CHECK_INTERVAL = 500;
12
+ const DISCONNECTED_INTERVAL = 1000;
13
+ const connectionDot = document.createElement("div");
14
+ connectionDot.style = `position: absolute; z-index: 9999; bottom: 10px; right: 10px; background-color: ${CONNECTING_COLOR}; width: 15px; height: 15px; border-radius: 50%; opacity: 0.8;`;
15
+ connectionDot.setAttribute("title", "Archival Dev Server: Connecting");
16
+ connectionDot.addEventListener(
17
+ "mouseenter",
18
+ () => (connectionDot.style.opacity = 0.2)
19
+ );
20
+ connectionDot.addEventListener(
21
+ "mouseleave",
22
+ () => (connectionDot.style.opacity = 0.8)
23
+ );
24
+
25
+ let lastContact = -1;
26
+ let isConnecting = false;
27
+ let connection;
28
+
29
+ function connectionLoop() {
30
+ connection.send(`page:${window.location.pathname}`);
31
+ if (Date.now() - lastContact > DISCONNECTED_INTERVAL) {
32
+ setConnected(false);
33
+ connectSocket();
34
+ }
35
+ setTimeout(connectionLoop, CHECK_INTERVAL);
36
+ }
37
+
38
+ function setConnected(connected) {
39
+ connectionDot.style.backgroundColor = connected
40
+ ? CONNECTED_COLOR
41
+ : CONNECTING_COLOR;
42
+ connectionDot.setAttribute(
43
+ "title",
44
+ `Archival Dev Server: ${connected ? "Connected" : "Disconnected"}`
45
+ );
46
+ }
47
+
48
+ window.onload = () => {
49
+ connectSocket(true);
50
+ };
51
+
52
+ function connectSocket(init) {
53
+ if (isConnecting) {
54
+ return;
55
+ }
56
+ isConnecting = true;
57
+ console.log(
58
+ `${init ? "connecting" : "reconnecting"} to archival dev server...`
59
+ );
60
+ document.body.appendChild(connectionDot);
61
+ connection = new WebSocket(`ws://localhost:${remotePort}`);
62
+ connection.onerror = () => {
63
+ isConnecting = false;
64
+ };
65
+
66
+ connection.onopen = () => {
67
+ isConnecting = false;
68
+ connection.send("connected");
69
+ if (init) {
70
+ connectionLoop();
71
+ }
72
+ };
73
+ connection.onmessage = (event) => {
74
+ lastContact = Date.now();
75
+ switch (event.data) {
76
+ case "ready":
77
+ console.log("connected to archival dev server.");
78
+ break;
79
+ case "ok":
80
+ setConnected(true);
81
+ break;
82
+ case "refresh":
83
+ window.location.reload();
84
+ break;
85
+ default:
86
+ console.log(`receieved unexpected message ${event.data}`);
87
+ break;
88
+ }
89
+ };
90
+ }
91
+ })();
@@ -12,7 +12,7 @@ module Archival
12
12
  attr_reader :page_templates
13
13
 
14
14
  def initialize(config, *_args)
15
- @config = Config.new(config)
15
+ @config = config
16
16
  refresh_config
17
17
  end
18
18
 
@@ -36,6 +36,11 @@ module Archival
36
36
  update_objects
37
37
  end
38
38
 
39
+ def full_rebuild
40
+ Layout.reset_cache
41
+ refresh_config
42
+ end
43
+
39
44
  def update_pages
40
45
  do_update_pages(File.join(@config.root, @config.pages_dir))
41
46
  end
@@ -61,6 +66,7 @@ module Archival
61
66
  add_prefix.call(page_name)
62
67
  )
63
68
  content = @file_system.read_template_file(template_file)
69
+ content += dev_mode_content if @config.dev_mode
64
70
  @page_templates[add_prefix.call(page_name)] =
65
71
  Liquid::Template.parse(content)
66
72
  end
@@ -122,6 +128,19 @@ module Archival
122
128
  file.write(render(template))
123
129
  end
124
130
  end
131
+ return if @config.dev_mode
132
+
133
+ # in production, also copy all assets to the dist folder.
134
+ @config.assets_dirs.each do |asset_dir|
135
+ FileUtils.copy_entry File.join(@config.root, asset_dir),
136
+ File.join(@config.build_dir, asset_dir)
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ def dev_mode_content
143
+ "<script src=\"http://localhost:#{@config.helper_port}/js/archival-helper.js\" type=\"application/javascript\"></script>" # rubocop:disable Layout/LineLength
125
144
  end
126
145
  end
127
146
  end
@@ -1,16 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tomlrb'
4
+
3
5
  module Archival
4
6
  class Config
5
- attr_reader :pages_dir, :objects_dir, :root, :build_dir
7
+ attr_reader :pages_dir, :objects_dir, :assets_dirs, :root, :build_dir,
8
+ :helper_port, :dev_mode
6
9
 
7
- def initialize(config)
8
- @pages_dir = config['pages'] || 'pages'
9
- @objects_dir = config['objects'] || 'objects'
10
+ def initialize(config = {})
10
11
  @root = config['root'] || Dir.pwd
11
- @build_dir = config['build_dir'] || File.join(
12
+ manifest = load_manifest
13
+ @pages_dir = config['pages'] || manifest['pages'] || 'pages'
14
+ @objects_dir = config['objects'] || manifest['objects'] || 'objects'
15
+ @build_dir = config['build_dir'] || manifest['build_dir'] || File.join(
12
16
  @root, 'dist'
13
17
  )
18
+ @helper_port = config['helper_port'] || manifest['helper_port'] || 2701
19
+ @assets_dirs = config['assets_dirs'] || manifest['assets'] || []
20
+ @dev_mode = config[:dev_mode] || false
21
+ end
22
+
23
+ def load_manifest
24
+ manifest_file = File.join(@root, 'manifest.toml')
25
+ return Tomlrb.load_file(manifest_file) if File.file? manifest_file
26
+
27
+ {}
14
28
  end
15
29
  end
16
30
  end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+ require 'open-uri'
5
+
6
+ module Archival
7
+ class HelperServer
8
+ attr_reader :page
9
+
10
+ def initialize(port, build_dir)
11
+ @port = port
12
+ @build_dir = build_dir
13
+ @helper_dir = File.expand_path(File.join(File.dirname(__FILE__),
14
+ '../../helper'))
15
+ end
16
+
17
+ def start
18
+ server = TCPServer.new @port
19
+ loop do
20
+ Thread.start(server.accept) do |client|
21
+ req = ''
22
+ method = nil
23
+ path = nil
24
+ while (line = client.gets) && (line != "\r\n")
25
+ unless method
26
+ req_info = line.split
27
+ method = req_info[0]
28
+ path = req_info[1]
29
+ end
30
+ req += line
31
+ end
32
+ client.close unless req
33
+ handle_request(client, req, method, path)
34
+ end
35
+ end
36
+ end
37
+
38
+ def refresh_client
39
+ ws_sendmessage('refresh')
40
+ end
41
+
42
+ private
43
+
44
+ MAGIC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
45
+
46
+ def handle_request(client, req, method, path)
47
+ if method == 'GET' && path.start_with?('/js/')
48
+ # For static paths, just serve the files they refer to.
49
+ http_response(client, type: 'application/javascript') do
50
+ serve_static(client, path)
51
+ end
52
+ client.close
53
+ elsif (matches = req.match(/^Sec-WebSocket-Key: (\S+)/))
54
+ websocket_key = matches[1]
55
+ # puts "Websocket handshake detected with key: #{websocket_key}"
56
+ connect_socket(client, websocket_key)
57
+ else
58
+ client.close
59
+ end
60
+ end
61
+
62
+ def connect_socket(client, websocket_key)
63
+ @socket = client
64
+ response_key = Digest::SHA1.base64digest([websocket_key,
65
+ MAGIC_GUID].join)
66
+ # puts "Responding to handshake with key: #{response_key}"
67
+
68
+ @socket.write "HTTP/1.1 101 Switching Protocols\r\n"
69
+ @socket.write "Upgrade: websocket\r\n"
70
+ @socket.write "Connection: Upgrade\r\n"
71
+ @socket.write "Sec-WebSocket-Accept: #{response_key}\r\n"
72
+ @socket.write "\r\n"
73
+
74
+ # puts 'Handshake completed.'
75
+ ws_loop
76
+ end
77
+
78
+ def ws_loop
79
+ loop do
80
+ msg = ws_getmessage
81
+ next unless msg
82
+
83
+ if msg == 'connected'
84
+ ws_sendmessage('ready')
85
+ elsif msg.start_with?('page:')
86
+ page_path = Pathname.new(msg.sub(/^page:/, ''))
87
+ @page = page_path.relative_path_from(@build_dir)
88
+ ws_sendmessage('ok')
89
+ end
90
+ end
91
+ end
92
+
93
+ def validate_ws_message
94
+ first_byte = @socket.getbyte
95
+ return unless first_byte
96
+
97
+ fin = first_byte & 0b10000000
98
+ opcode = first_byte & 0b00001111
99
+
100
+ # Our server only supports single-frame, text messages.
101
+ # Raise an exception if the client tries to send anything else.
102
+ raise "We don't support continuations" unless fin
103
+ raise 'We only support opcode 1' unless opcode == 1
104
+
105
+ second_byte = @socket.getbyte
106
+ is_masked = second_byte & 0b10000000
107
+ payload_size = second_byte & 0b01111111
108
+
109
+ raise 'frame masked incorrectly' unless is_masked
110
+ raise 'payload must be < 126 bytes in length' unless payload_size < 126
111
+
112
+ payload_size
113
+ end
114
+
115
+ def ws_getmessage
116
+ payload_size = validate_ws_message
117
+ return unless payload_size
118
+
119
+ # warn "Payload size: #{payload_size} bytes"
120
+
121
+ mask = 4.times.map { @socket.getbyte }
122
+ # warn "Got mask: #{mask.inspect}"
123
+
124
+ data = payload_size.times.map { @socket.getbyte }
125
+ # warn "Got masked data: #{data.inspect}"
126
+
127
+ unmasked_data = data.each_with_index.map do |byte, i|
128
+ byte ^ mask[i % 4]
129
+ end
130
+ # warn "Unmasked the data: #{unmasked_data.inspect}"
131
+
132
+ unmasked_data.to_s.pack('C*').force_encoding('utf-8')
133
+ end
134
+
135
+ def ws_sendmessage(message)
136
+ return unless @socket
137
+
138
+ output = [0b10000001, message.size, message]
139
+ @socket.write output.pack("CCA#{message.size}")
140
+ end
141
+
142
+ def serve_static(client, path)
143
+ buffer = File.open(File.join(@helper_dir, path)).read
144
+ buffer.sub! '$PORT', @port.to_s
145
+ client.print buffer
146
+ end
147
+
148
+ def http_response(client, config)
149
+ status = config[:status] ||= 200
150
+ type = config[:type] ||= 'text/html'
151
+ client.print "HTTP/1.1 #{status}\r\n"
152
+ client.print "Content-Type: #{type}\r\n"
153
+ client.print "\r\n"
154
+ yield
155
+ end
156
+ end
157
+ end
@@ -4,40 +4,83 @@ require 'listen'
4
4
  require 'pathname'
5
5
 
6
6
  module Archival
7
- def self.child?(parent, child)
8
- path = Pathname.new(child)
9
- return true if path.fnmatch?(File.join(parent, '**'))
10
-
11
- false
7
+ def listen(config = {})
8
+ @config = Config.new(config.merge(dev_mode: true))
9
+ builder = Builder.new(@config)
10
+ Logger.benchmark('built') do
11
+ builder.write_all
12
+ end
13
+ ignore = %r{/dist/}
14
+ listener = Listen.to(@config.root,
15
+ ignore: ignore) do |modified, added, removed|
16
+ updated_pages = []
17
+ updated_objects = []
18
+ updated_assets = []
19
+ (modified + added + removed).each do |file|
20
+ case change_type(file)
21
+ when :pages
22
+ updated_pages << file
23
+ when :objects
24
+ updated_objects << file
25
+ when :assets
26
+ updated_assets << file
27
+ end
28
+ end
29
+ @server.refresh_client if rebuild?(builder, updated_objects,
30
+ updated_pages, updated_assets)
31
+ end
32
+ listener.start
33
+ serve_helpers
12
34
  end
13
35
 
14
- def self.process_change?(file, builder)
15
- if child?(File.join(@config.root, @config.pages_dir), file)
36
+ module_function :listen
37
+
38
+ class << self
39
+ private
40
+
41
+ def child?(parent, child)
42
+ path = Pathname.new(child)
43
+ return true if path.fnmatch?(File.join(parent, '**'))
44
+
45
+ false
46
+ end
47
+
48
+ def change_type(file)
16
49
  # a page was modified, rebuild the pages.
17
- builder.update_pages
18
- return true
19
- elsif child?(File.join(@config.root, @config.objects_dir), file)
50
+ return :pages if child?(File.join(@config.root, @config.pages_dir),
51
+ file)
20
52
  # an object was modified, rebuild the objects.
21
- builder.update_objects
22
- return true
53
+ return :objects if child?(File.join(@config.root, @config.objects_dir),
54
+ file)
55
+
56
+ # layout and other assets. For now, this is everything.
57
+ @config.assets_dirs.each do |dir|
58
+ return :assets if child?(File.join(@config.root, dir), file)
59
+ end
60
+ return :assets if child?(File.join(@config.root, 'layout'), file)
61
+ return :assets if ['manifest.toml',
62
+ 'objects.toml'].include? File.basename(file)
63
+
64
+ :none
23
65
  end
24
- false
25
- end
26
66
 
27
- def listen(config)
28
- @config = Config.new(config)
29
- builder = Builder.new(config)
30
- builder.write_all
31
- listener = Listen.to(@config.root) do |modified, added, removed|
32
- needs_update = false
33
- (modified + added + removed).each do |file|
34
- needs_update = true if process_change?(file, builder)
67
+ def rebuild?(builder, updated_objects, updated_pages, updated_assets)
68
+ if updated_pages.empty? && updated_objects.empty? && updated_assets.empty?
69
+ return false
70
+ end
71
+
72
+ Logger.benchmark('rebuilt') do
73
+ builder.update_objects if updated_objects.length
74
+ builder.update_pages if updated_pages.length
75
+ builder.full_rebuild if updated_assets.length
76
+ builder.write_all
35
77
  end
36
- builder.write_all if needs_update
78
+ true
37
79
  end
38
- listener.start
39
- listener
40
- end
41
80
 
42
- module_function :listen
81
+ def serve_helpers
82
+ @server = HelperServer.new(@config.helper_port, @config.build_dir)
83
+ @server.start
84
+ end
85
+ end
43
86
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+
5
+ module Archival
6
+ class Logger
7
+ def self.benchmark(message, &block)
8
+ Benchmark.bm do |bm|
9
+ bm.report(message, &block)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -27,19 +27,15 @@ class RakeTasks
27
27
  build_dir = Dir.pwd
28
28
 
29
29
  task 'build' do
30
- builder = Archival::Builder.new('root' => build_dir)
31
- builder.write_all
30
+ Archival::Logger.benchmark('built') do
31
+ config = Archival::Config.new('root' => build_dir)
32
+ builder = Archival::Builder.new(config)
33
+ builder.write_all
34
+ end
32
35
  end
33
36
 
34
37
  task 'run' do
35
- Archival.listen(build_dir)
36
- begin
37
- sleep
38
- rescue Interrupt
39
- # Don't print a stack when a user interrupts, as this is the right way
40
- # to stop the development server.
41
- puts ''
42
- end
38
+ Archival.listen('root' => build_dir)
43
39
  end
44
40
 
45
41
  RakeTasks.instance = self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Archival
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.4'
5
5
  end
data/lib/archival.rb CHANGED
@@ -5,6 +5,8 @@ module Archival
5
5
  end
6
6
 
7
7
  require 'archival/version'
8
+ require 'archival/logger'
8
9
  require 'archival/config'
10
+ require 'archival/helper_server'
9
11
  require 'archival/builder'
10
12
  require 'archival/listen'
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archival",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "An incredibly simple CMS for durable websites",
5
5
  "bin": "build.rb",
6
6
  "directories": {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archival
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesse Ditson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-05 00:00:00.000000000 Z
11
+ date: 2021-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -82,10 +82,13 @@ files:
82
82
  - bin/ruby-rewrite
83
83
  - bin/setup
84
84
  - exe/archival
85
+ - helper/js/archival-helper.js
85
86
  - lib/archival.rb
86
87
  - lib/archival/builder.rb
87
88
  - lib/archival/config.rb
89
+ - lib/archival/helper_server.rb
88
90
  - lib/archival/listen.rb
91
+ - lib/archival/logger.rb
89
92
  - lib/archival/rake_tasks.rb
90
93
  - lib/archival/version.rb
91
94
  - lib/tags/layout.rb