summaryrefslogtreecommitdiff
path: root/core.rb
diff options
context:
space:
mode:
Diffstat (limited to 'core.rb')
-rw-r--r--core.rb85
1 files changed, 85 insertions, 0 deletions
diff --git a/core.rb b/core.rb
new file mode 100644
index 0000000..5dad22a
--- /dev/null
+++ b/core.rb
@@ -0,0 +1,85 @@
+class Op
+ def check
+ raise "#{self.class} doesn't have a (required) 'check' method"
+ end
+ def do
+ raise "#{self.class} doesn't have a (required) 'do' method"
+ end
+ def undo
+ raise "#{self.class} doesn't have a (required) 'undo' method"
+ end
+end
+
+def defop(name, args, &block)
+ name = name.to_sym
+ eval "class #{name} < Op; end" # Must use `eval' to access
+ cls = eval "#{name}" # the global namespace
+ cls.instance_eval do
+ args.map {|a| attr_reader a.to_sym }
+ end
+ cls.define_method :initialize do |**kwargs|
+ args.each {|arg| instance_variable_set :"@#{arg}", kwargs[arg.to_sym] }
+ end
+ cls.define_method :to_s do inspect end
+ cls.define_method :to_str do to_s end
+ cls.instance_eval &block
+end
+
+defop :Write, [:file, :contents] do
+ define_method :check do
+ if File.exist? @file
+ if (File.read @file) != @contents
+ raise "File `#{@file}' exists but has unexpected contents"
+ else true
+ end
+ end
+ end
+ define_method :do do
+ write @file, @contents
+ end
+ define_method :undo do
+ rm @file
+ end
+end
+
+defop :Deploy, [:ops, :save] do
+ define_method :initialize do |save: nil, &block|
+ @ops = []
+ @save = save
+ self.instance_eval &block
+ end
+ define_method :check do
+ @ops.all? {|op| op.check } and
+ (@save ? self.save.check : true)
+ end
+ define_method :do do
+ self.load.undo if @save && self.save.check
+ @ops.each {|op| op.check || op.do }
+ self.save.do if @save
+ end
+ define_method :undo do
+ @ops.reverse.each {|op| op.check && op.undo }
+ self.save.undo if @save
+ end
+ define_method :save do
+ (Write.new file: @save,
+ contents: self.to_yaml)
+ end
+ define_method :load do
+ YAML.load_file(
+ @save,
+ permitted_classes: ([Op] + Op.subclasses))
+ end
+end
+
+def deploy(...)
+ Deploy.new(...)
+end
+
+def defdeploy(name, &block)
+ Deploy.define_method name, &block
+end
+
+defdeploy :op do |cls, **args|
+ @ops.push cls.new(**args)
+end