summaryrefslogtreecommitdiffstats
path: root/lib/blink
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2005-06-27 21:44:46 +0000
committerLuke Kanies <luke@madstop.com>2005-06-27 21:44:46 +0000
commit8f95084cd854aef4e3493854e58cefd352cdc68d (patch)
treef31288d1cbbd60c0fdc7c04bbd6960516a6893be /lib/blink
parent6f074138779e558fd7017880f606dcf3527233f9 (diff)
downloadpuppet-8f95084cd854aef4e3493854e58cefd352cdc68d.tar.gz
puppet-8f95084cd854aef4e3493854e58cefd352cdc68d.tar.xz
puppet-8f95084cd854aef4e3493854e58cefd352cdc68d.zip
renaming blink to puppet
git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@302 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/blink')
-rw-r--r--lib/blink/client.rb120
-rw-r--r--lib/blink/element.rb41
-rw-r--r--lib/blink/event.rb201
-rw-r--r--lib/blink/fact.rb65
-rw-r--r--lib/blink/function.rb72
-rw-r--r--lib/blink/message.rb64
-rw-r--r--lib/blink/selector.rb82
-rw-r--r--lib/blink/statechange.rb111
-rw-r--r--lib/blink/storage.rb48
-rw-r--r--lib/blink/transaction.rb162
-rw-r--r--lib/blink/transportable.rb200
-rw-r--r--lib/blink/type.rb851
-rw-r--r--lib/blink/type/component.rb74
-rw-r--r--lib/blink/type/file.rb450
-rw-r--r--lib/blink/type/package.rb366
-rw-r--r--lib/blink/type/process.rb83
-rw-r--r--lib/blink/type/service.rb186
-rw-r--r--lib/blink/type/state.rb135
-rw-r--r--lib/blink/type/symlink.rb109
-rw-r--r--lib/blink/type/typegen.rb146
-rw-r--r--lib/blink/type/typegen/filerecord.rb243
-rw-r--r--lib/blink/type/typegen/filetype.rb316
22 files changed, 0 insertions, 4125 deletions
diff --git a/lib/blink/client.rb b/lib/blink/client.rb
deleted file mode 100644
index f266d2dfb..000000000
--- a/lib/blink/client.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# the available clients
-
-require 'blink'
-require 'blink/function'
-require 'blink/type'
-require 'blink/fact'
-require 'blink/transaction'
-require 'blink/transportable'
-require 'http-access2'
-require 'soap/rpc/driver'
-require 'soap/rpc/httpserver'
-#require 'webrick/https'
-require 'logger'
-
-module Blink
- class ClientError < RuntimeError; end
- #---------------------------------------------------------------
- class Client < SOAP::RPC::HTTPServer
- def initialize(hash)
- # to whom do we connect?
- @server = nil
- @nil = nil
- @url = hash[:Server]
- if hash.include?(:Listen) and hash[:Listen] == false
- Blink.notice "We're a local client"
- @localonly = true
- @driver = @url
- else
- Blink.notice "We're a networked client"
- @localonly = false
- @driver = SOAP::RPC::Driver.new(@url, 'urn:blink-server')
- @driver.add_method("getconfig", "name")
- end
- unless @localonly
- hash.delete(:Server)
-
- Blink.notice "Server is %s" % @url
-
- hash[:BindAddress] ||= "0.0.0.0"
- hash[:Port] ||= 17444
- hash[:Debug] ||= true
- hash[:AccessLog] ||= []
-
- super(hash)
- end
- end
-
- def getconfig
- Blink.debug "server is %s" % @url
- #client.loadproperty('files/sslclient.properties')
- Blink.notice("getting config")
- objects = nil
- if @localonly
- objects = @driver.getconfig(self)
- else
- objects = @driver.getconfig(Blink::Fact["hostname"])
- end
- self.config(objects)
- end
-
- # this method is how the client receives the tree of Transportable
- # objects
- # for now, just descend into the tree and perform and necessary
- # manipulations
- def config(tree)
- Blink.notice("Calling config")
- container = Marshal::load(tree).to_type
-
- # this is a gross hack... but i don't see a good way around it
- # set all of the variables to empty
- Blink::Transaction.init
- # for now we just evaluate the top-level container, but eventually
- # there will be schedules and such associated with each object,
- # and probably with the container itself
- transaction = container.evaluate
- #transaction = Blink::Transaction.new(objects)
- transaction.toplevel = true
- transaction.evaluate
- self.shutdown
- end
-
- def callfunc(name,args)
- Blink.notice("Calling callfunc on %s" % name)
- if function = Blink::Function[name]
- #Blink.debug("calling function %s" % function)
- value = function.call(args)
- #Blink.debug("from %s got %s" % [name,value])
- return value
- else
- raise "Function '%s' not found" % name
- end
- end
-
- private
-
- def on_init
- @default_namespace = 'urn:blink-client'
- add_method(self, 'config', 'config')
- add_method(self, 'callfunc', 'name', 'arguments')
- end
-
- def cert(filename)
- OpenSSL::X509::Certificate.new(File.open(File.join(@dir, filename)) { |f|
- f.read
- })
- end
-
- def key(filename)
- OpenSSL::PKey::RSA.new(File.open(File.join(@dir, filename)) { |f|
- f.read
- })
- end
-
- end
- #---------------------------------------------------------------
-end
diff --git a/lib/blink/element.rb b/lib/blink/element.rb
deleted file mode 100644
index dd187977a..000000000
--- a/lib/blink/element.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# included so we can test object types
-require 'blink'
-
-
-#---------------------------------------------------------------
-# the base class for both types and states
-# very little functionality; basically just defines the interface
-# and provides a few simple across-the-board functions like 'noop'
-class Blink::Element
- attr_writer :noop
-
- #---------------------------------------------------------------
- # all of our subclasses must respond to each of these methods...
- @@interface_methods = [
- :retrieve, :insync?, :sync, :fqpath, :evaluate
- ]
-
- # so raise an error if a method that isn't overridden gets called
- @@interface_methods.each { |method|
- self.send(:define_method,method) {
- raise "%s has not overridden %s" % [self.class,method]
- }
- }
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # for testing whether we should actually do anything
- def noop
- unless defined? @noop
- @noop = false
- end
- return @noop || Blink[:noop] || false
- end
- #---------------------------------------------------------------
-
-end
-#---------------------------------------------------------------
diff --git a/lib/blink/event.rb b/lib/blink/event.rb
deleted file mode 100644
index 2cdda9976..000000000
--- a/lib/blink/event.rb
+++ /dev/null
@@ -1,201 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# included so we can test object types
-require 'blink'
-require 'blink/type'
-
-module Blink
- # events are transient packets of information; they result in one or more (or none)
- # subscriptions getting triggered, and then they get cleared
- # eventually, these will be passed on to some central event system
- class Event
- # subscriptions are permanent associations determining how different
- # objects react to an event
- class Subscription
- attr_accessor :source, :event, :target, :method
-
- def initialize(hash)
- @triggered = false
-
- hash.each { |method,value|
- # assign each value appropriately
- # this is probably wicked-slow
- self.send(method.to_s + "=",value)
- }
- Blink.warning "New Subscription: '%s' => '%s'" %
- [@source,@event]
- end
-
- # the transaction is passed in so that we can notify it if
- # something fails
- def trigger(transaction)
- # this is potentially incomplete, because refreshing an object
- # could theoretically kick off an event, which would not get run
- # or, because we're executing the first subscription rather than
- # the last, a later-refreshed object could somehow be connected
- # to the "old" object rather than "new"
- # but we're pretty far from that being a problem
- if transaction.triggercount(self) > 0
- Blink.verbose "%s has already run" % self
- else
- Blink.verbose "'%s' matched '%s'; triggering '%s' on '%s'" %
- [@source,@event,@method,@target]
- begin
- if @target.respond_to?(@method)
- @target.send(@method)
- else
- Blink.verbose "'%s' of type '%s' does not respond to '%s'" %
- [@target,@target.class,@method.inspect]
- end
- rescue => detail
- # um, what the heck do i do when an object fails to refresh?
- # shouldn't that result in the transaction rolling back?
- # XXX yeah, it should
- Blink.error "'%s' failed to %s: '%s'" %
- [@target,@method,detail]
- raise
- #raise "We need to roll '%s' transaction back" %
- #transaction
- end
- transaction.triggered(self)
- end
- end
- end
-
- attr_accessor :event, :object, :transaction
-
- @@events = []
-
- @@subscriptions = []
-
- def Event.process
- Blink.warning "Processing events"
- @@events.each { |event|
- @@subscriptions.find_all { |sub|
- #Blink.warning "Sub source: '%s'; event object: '%s'" %
- # [sub.source.inspect,event.object.inspect]
- sub.source == event.object and
- (sub.event == event.event or
- sub.event == :ALL_EVENTS)
- }.each { |sub|
- Blink.notice "Found sub"
- sub.trigger(event.transaction)
- }
- }
-
- @@events.clear
- end
-
- def Event.subscribe(hash)
- if hash[:event] == '*'
- hash[:event] = :ALL_EVENTS
- end
- sub = Subscription.new(hash)
-
- # add to the correct area
- @@subscriptions.push sub
- end
-
- def initialize(args)
- unless args.include?(:event) and args.include?(:object)
- raise "Event.new called incorrectly"
- end
-
- @event = args[:event]
- @object = args[:object]
- @transaction = args[:transaction]
-
- Blink.warning "New Event: '%s' => '%s'" %
- [@object,@event]
-
- # initially, just stuff all instances into a central bucket
- # to be handled as a batch
- @@events.push self
- end
- end
-end
-
-
-#---------------------------------------------------------------
-# here i'm separating out the methods dealing with handling events
-# currently not in use, so...
-
-class Blink::NotUsed
- #---------------------------------------------------------------
- # return action array
- # these are actions to use for responding to events
- # no, this probably isn't the best way, because we're providing
- # access to the actual hash, which is silly
- def action
- if not defined? @actions
- puts "defining action hash"
- @actions = Hash.new
- end
- @actions
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # call an event
- # this is called on subscribers by the trigger method from the obj
- # which sent the event
- # event handling should probably be taking place in a central process,
- # but....
- def event(event,obj)
- Blink.debug "#{self} got event #{event} from #{obj}"
- if @actions.key?(event)
- Blink.debug "calling it"
- @actions[event].call(self,obj,event)
- else
- p @actions
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # subscribe to an event or all events
- # this entire event system is a hack job and needs to
- # be replaced with a central event handler
- def subscribe(args,&block)
- obj = args[:object]
- event = args[:event] || '*'.intern
- if obj.nil? or event.nil?
- raise "subscribe was called wrongly; #{obj} #{event}"
- end
- obj.action[event] = block
- #events.each { |event|
- unless @notify.key?(event)
- @notify[event] = Array.new
- end
- unless @notify[event].include?(obj)
- Blink.debug "pushing event '%s' for object '%s'" % [event,obj]
- @notify[event].push(obj)
- end
- # }
- #else
- # @notify['*'.intern].push(obj)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # initiate a response to an event
- def trigger(event)
- subscribers = Array.new
- if @notify.include?('*') and @notify['*'].length > 0
- @notify['*'].each { |obj| subscribers.push(obj) }
- end
- if (@notify.include?(event) and (! @notify[event].empty?) )
- @notify[event].each { |obj| subscribers.push(obj) }
- end
- Blink.debug "triggering #{event}"
- subscribers.each { |obj|
- Blink.debug "calling #{event} on #{obj}"
- obj.event(event,self)
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
-end # Blink::Type
diff --git a/lib/blink/fact.rb b/lib/blink/fact.rb
deleted file mode 100644
index 2b46aabd8..000000000
--- a/lib/blink/fact.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# an interface for registering and retrieving facts
-# this is an abstract interface, and should just be used to interact
-# with another library
-
-# currently a very thin veneer on 'facter'
-
-require 'facter'
-require 'blink'
-require 'blink/type'
-
-module Blink
- class Fact
- def Fact.[](name)
- fact = Facter[name]
- if fact.value.nil?
- raise "Could not retrieve fact %s" % name
- end
- Blink.debug("fact: got %s from %s for %s" % [fact.value,fact,name])
- return fact.value
- end
-
- # just pass the block to 'add'
- # the block has to do things like set the interpreter,
- # the code (which can be a ruby block), and maybe the
- # os and osrelease
- def Fact.add(name,&block)
- Facter[name].add(&block)
- end
-
- def Fact.name
- return :fact
- end
-
- def Fact.namevar
- return :name
- end
-
- #Blink::Type.newtype(self)
-
- # we're adding a new resolution mechanism here; this is just how
- # types work
- # we don't have any real interest in the returned object
- def initialize(hash)
- name = hash[:name]
- hash.delete(:name)
- Fact.add(name) { |fact|
- method = nil
- hash.each { |key,value|
- if key.is_a?(String)
- method = key + "="
- elsif key.is_a?(Symbol)
- method = key.id2name + "="
- else
- raise "Key must be either string or symbol"
- end
- fact.send(method,value)
- }
- }
- end
- end
-end
diff --git a/lib/blink/function.rb b/lib/blink/function.rb
deleted file mode 100644
index d5cc11107..000000000
--- a/lib/blink/function.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'blink'
-require 'blink/fact'
-
-module Blink
- class Function
- @@functions = Hash.new(nil)
-
- #---------------------------------------------------------------
- def Function.[](name)
- return @@functions[name]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def call(args)
- @code.call(args)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # we want a 'proc' item instead of a block, so that we can return
- # from it
- def initialize(name,code)
- @name = name
- @code = code
-
- @@functions[name] = self
- end
- #---------------------------------------------------------------
- end
-
- Function.new("fact", proc { |fact|
- require 'blink/fact'
-
- value = Fact[fact]
- Blink.debug("retrieved %s as %s" % [fact,value])
- value
- })
-
- Function.new("addfact", proc { |args|
- require 'blink/fact'
- #Blink.debug("running addfact")
-
- hash = nil
- if args.is_a?(Array)
- hash = Hash[*args]
- end
- name = nil
- if hash.has_key?("name")
- name = hash["name"]
- hash.delete("name")
- elsif hash.has_key?(:name)
- name = hash[:name]
- hash.delete(:name)
- else
- raise "Functions must have names"
- end
- #Blink.debug("adding fact %s" % name)
- newfact = Fact.add(name) { |fact|
- hash.each { |key,value|
- method = key + "="
- fact.send(method,value)
- }
- }
-
- #Blink.debug("got fact %s" % newfact)
- })
-end
diff --git a/lib/blink/message.rb b/lib/blink/message.rb
deleted file mode 100644
index 31d5fa503..000000000
--- a/lib/blink/message.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# $Id$
-
-module Blink
- #------------------------------------------------------------
- # provide feedback of various types to the user
- # modeled after syslog messages
- # each level of message prints in a different color
- class Message
- @@messages = Array.new
- @@levels = [ :debug, :verbose, :notice, :warning, :error ]
- @@colors = {
- :debug => SLATE,
- :verbose => ORANGE,
- :notice => PINK,
- :warning => GREEN,
- :error => YELLOW
- }
-
- attr_accessor :level, :message, :source
-
- def Message.loglevels
- return @@levels
- end
-
- def initialize(args)
- unless args.include?(:level) && args.include?(:message) &&
- args.include?(:source)
- raise "Blink::Message called incorrectly"
- end
-
- if args[:level].class == String
- @level = args[:level].intern
- elsif args[:level].class == Symbol
- @level = args[:level]
- else
- raise "Level is not a string or symbol: #{args[:level].class}"
- end
- @message = args[:message]
- @source = args[:source]
- @time = Time.now
- # this should include the host name, and probly lots of other
- # stuff, at some point
- unless @@levels.include?(level)
- raise "Invalid message level #{level}"
- end
-
- @@messages.push(self)
- Blink.newmessage(self)
- end
-
- def to_s
- # this probably won't stay, but until this leaves the console,
- # i'm going to use coloring...
- #return "#{@time} #{@source} (#{@level}): #{@message}"
- #return @@colors[@level] + "%s %s (%s): %s" % [
- # @time, @source, @level, @message
- #] + RESET
- return @@colors[@level] + "%s (%s): %s" % [
- @source, @level, @message
- ] + RESET
- end
- end
- #------------------------------------------------------------
-end
diff --git a/lib/blink/selector.rb b/lib/blink/selector.rb
deleted file mode 100644
index 51e82b09d..000000000
--- a/lib/blink/selector.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'blink'
-require 'blink/fact'
-
-module Blink
- #---------------------------------------------------------------
- # this class will provide something like a 'select' statement, but it will
- # return a value
- # it will be used something like this:
- # value = Selector.new(
- # proc { test() } => value,
- # proc { test2() } => value2,
- # )
-
- # each test gets evaluated in turn; the first one to return true has its
- # value returned as the value of the statement
- # this will be used to provide abstraction in objects, but it's currently
- # unused
-
- class Selector < Array
- attr_accessor :default
-
- def add(value,&block)
- option = Option.new(value,&block)
- @ohash[value] = option
- @oarray.push(option)
- end
-
- def evaluate
- @oarray.each { |option|
- if option.true?
- return option.value
- end
- }
- return nil
- end
-
- # we have to support providing different values based on
- # different criteria, e.g., default is X, SunOS gets Y, and
- # host Yayness gets Z.
- # thus, no invariant
- def initialize
- @oarray = []
- @ohash = {}
-
- if block_given?
- yield self
- end
- end
-
- def to_s
- return self.evaluate()
- end
-
- class Option
- attr_accessor :value, :test, :invariant
-
- def initialize(value,&block)
- @value = value
- @test = block
- end
-
- def to_s
- if self.evaluate
- return value
- end
- end
-
- def true?
- unless @test.is_a?(Proc)
- raise "Cannot yet evaluate non-code tests"
- end
-
- return @test.call()
- end
- end
- #---------------------------------------------------------------
- end
-end
diff --git a/lib/blink/statechange.rb b/lib/blink/statechange.rb
deleted file mode 100644
index f88108d8a..000000000
--- a/lib/blink/statechange.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# the class responsible for actually doing any work
-
-# enables no-op and logging/rollback
-
-module Blink
- class StateChange
- attr_accessor :is, :should, :type, :path, :state, :transaction, :run
-
- #---------------------------------------------------------------
- def initialize(state)
- @state = state
- #@state.parent.newchange
- @path = state.fqpath
- @is = state.is
- @should = state.should
-
- @run = false
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def go
- if @state.noop
- #Blink.notice "%s is noop" % @state
- return nil
- end
-
- begin
- event = @state.sync
- @run = true
-
- # default to a simple event type
- if event.nil?
- event = @state.parent.class.name.id2name + "_changed"
- elsif ! event.is_a?(Symbol)
- Blink.notice "State '%s' returned invalid event '%s'; resetting to default" %
- [@state.class,event]
-
- event = @state.parent.class.name.id2name + "_changed"
- end
-
- # i should maybe include object type, but the event type
- # should basically point to that, right?
- return Blink::Event.new(
- :event => event,
- :object => @state.parent,
- :transaction => @transaction,
- :message => self.to_s
- )
- rescue => detail
- Blink.error "%s failed: %s" % [self.to_s,detail]
- raise
- # there should be a way to ask the state what type of event
- # it would have generated, but...
- pname = @state.parent.class.name.id2name
- #if pname.is_a?(Symbol)
- # pname = pname.id2name
- #end
- return Blink::Event.new(
- :event => pname + "_failed",
- :object => @state.parent,
- :transaction => @transaction,
- :message => "Failed: " + self.to_s
- )
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def forward
- #Blink.notice "moving change forward"
-
- unless defined? @transaction
- raise "StateChange '%s' tried to be executed outside of transaction" %
- self
- end
-
- return self.go
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def backward
- @state.should = @is
- @state.retrieve
-
- Blink.notice "Rolling %s backward" % self
- return self.go
-
- #raise "Moving statechanges backward is currently unsupported"
- #@type.change(@path,@should,@is)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def noop
- return @state.noop
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def to_s
- return "%s: %s => %s" % [@state,@is,@should]
- end
- #---------------------------------------------------------------
- end
-end
diff --git a/lib/blink/storage.rb b/lib/blink/storage.rb
deleted file mode 100644
index 9fc21d38b..000000000
--- a/lib/blink/storage.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-# $Id$
-
-module Blink
- # a class for storing state
- class Storage
- include Singleton
- @@state = Hash.new { |hash,key|
- hash[key] = Hash.new(nil)
- }
- @@splitchar = "\t"
-
- def initialize
- self.class.load
- end
-
- def Storage.load
- # XXX I should probably use a better default state dir
- Blink[:statefile] ||= "/var/tmp/blinkstate"
- return unless File.exists?(Blink[:statefile])
- File.open(Blink[:statefile]) { |file|
- file.gets { |line|
- myclass, key, value = line.split(@@splitchar)
-
- @@state[myclass][key] = Marshal::load(value)
- }
- }
- end
-
- def Storage.state(myclass)
- unless myclass.is_a? Class
- myclass = myclass.class
- end
- result = @@state[myclass]
- return result
- end
-
- def Storage.store
- File.open(Blink[:statefile], File::CREAT|File::WRONLY, 0600) { |file|
- @@state.each { |klass, thash|
- thash.each { |key,value|
- mvalue = Marshal::dump(value)
- file.puts([klass,key,mvalue].join(@@splitchar))
- }
- }
- }
- end
- end
-end
diff --git a/lib/blink/transaction.rb b/lib/blink/transaction.rb
deleted file mode 100644
index 87b2f950c..000000000
--- a/lib/blink/transaction.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# the class that actually walks our object/state tree, collects the changes,
-# and performs them
-
-# there are two directions of walking:
-# - first we recurse down the tree and collect changes
-# - then we walk back up the tree through 'refresh' after the changes
-
-require 'blink'
-require 'blink/statechange'
-
-#---------------------------------------------------------------
-module Blink
-class Transaction
- attr_accessor :toplevel, :component
-
- #---------------------------------------------------------------
- # a bit of a gross hack; a global list of objects that have failed to sync,
- # so that we can verify during later syncs that our dependencies haven't
- # failed
- def Transaction.init
- @@failures = Hash.new(0)
- @@changed = []
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # for now, just store the changes for executing linearly
- # later, we might execute them as we receive them
- def change(change)
- @changes.push change
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # i don't need to worry about ordering, because it's not possible to specify
- # an object as a dependency unless it's already been mentioned within the language
- # thus, an object gets defined, then mentioned as a dependency, and the objects
- # are synced in that order automatically
- def evaluate
- Blink.notice "executing %s changes or transactions" % @changes.length
-
- return @changes.collect { |change|
- if change.is_a?(Blink::StateChange)
- change.transaction = self
- events = nil
- begin
- events = [change.forward].flatten
- #@@changed.push change.state.parent
- rescue => detail
- Blink.error("%s failed: %s" % [change,detail])
- raise
- # at this point, we would normally do error handling
- # but i haven't decided what to do for that yet
- # so just record that a sync failed for a given object
- #@@failures[change.state.parent] += 1
- # this still could get hairy; what if file contents changed,
- # but a chmod failed? how would i handle that error? dern
- end
-
- if events.nil?
- Blink.verbose "No events returned?"
- else
- # first handle the subscriptions on individual objects
- events.each { |event|
- change.state.parent.subscribers?(event).each { |sub|
- sub.trigger(self)
- }
- }
- end
- events
- elsif change.is_a?(Blink::Transaction)
- change.evaluate
- else
- raise "Transactions cannot handle objects of type %s" % child.class
- end
- }.flatten.reject { |event|
- event.nil?
- }.each { |event|
- # this handles subscriptions on the components, rather than
- # on idividual objects
- self.component.subscribers?(event).each { |sub|
- sub.trigger(self)
- }
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this should only be called by a Blink::Container object now
- # and it should only receive an array
- def initialize(tree)
- @tree = tree
- @toplevel = false
-
- @triggered = Hash.new(0)
-
- # of course, this won't work on the second run
- unless defined? @@failures
- @toplevel = true
- self.class.init
- end
- # change collection is in-band, and message generation is out-of-band
- # of course, exception raising is also out-of-band
- @changes = @tree.collect { |child|
- # these children are all Blink::Type instances
- # not all of the children will return a change, and Containers
- # return transactions
- child.evaluate
- }.flatten.reject { |child|
- child.nil? # remove empties
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def rollback
- @changes.each { |change|
- if change.is_a?(Blink::StateChange)
- next unless change.run
- #change.transaction = self
- begin
- change.backward
- #@@changed.push change.state.parent
- rescue => detail
- Blink.error("%s rollback failed: %s" % [change,detail])
- # at this point, we would normally do error handling
- # but i haven't decided what to do for that yet
- # so just record that a sync failed for a given object
- #@@failures[change.state.parent] += 1
- # this still could get hairy; what if file contents changed,
- # but a chmod failed? how would i handle that error? dern
- end
- elsif change.is_a?(Blink::Transaction)
- # yay, recursion
- change.rollback
- else
- raise "Transactions cannot handle objects of type %s" % child.class
- end
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def triggercount(sub)
- Blink.notice "Triggercount on %s is %s" % [sub,@triggered[sub]]
- return @triggered[sub]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def triggered(sub)
- @triggered[sub] += 1
- Blink.notice "%s was triggered; count is %s" % [sub,@triggered[sub]]
- end
- #---------------------------------------------------------------
-end
-end
-#---------------------------------------------------------------
diff --git a/lib/blink/transportable.rb b/lib/blink/transportable.rb
deleted file mode 100644
index 522a34d7c..000000000
--- a/lib/blink/transportable.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'blink'
-
-module Blink
- #------------------------------------------------------------
- class TransObject < Hash
- attr_accessor :type
-
- @@ohash = {}
- @@oarray = []
-
- def TransObject.add(object)
- @@oarray.push object
-
- # this is just so we can check, at parse time, whether a required
- # object has already been mentioned when it is listed as required
- # because we're ordered, as long as an object gets made before its
- # dependent objects will get synced later
- @@ohash[object.longname] = object
- end
-
- def TransObject.clear
- @@oarray.clear
- end
-
- def TransObject.list
- return @@oarray
- end
-
- def initialize(name,type)
- self[:name] = name
- @type = type
- self.class.add(self)
- end
-
- def longname
- return [self.type,self[:name]].join('--')
- end
-
- def name
- return self[:name]
- end
-
- def to_s
- return "%s(%s) => %s" % [@type,self[:name],super]
- end
-
- def to_type
- retobj = nil
- if type = Blink::Type.type(self.type)
- #begin
- # this will fail if the type already exists
- # which may or may not be a good thing...
- retobj = type.new(self)
- #rescue => detail
- # Blink.error "Failed to create %s: %s" % [type.name,detail]
- # puts self.class
- # puts self.inspect
- # exit
- #end
- else
- raise "Could not find object type %s" % self.type
- end
-
- return retobj
- end
- end
- #------------------------------------------------------------
-
- #------------------------------------------------------------
- class TransSetting
- attr_accessor :type, :name, :args, :evalcount
-
- def initialize
- @evalcount = 0
- end
-
- def evaluate
- @evalcount += 0
- if type = Blink::Type.type(self.type)
- # call the settings
- name = self.name
- unless name.is_a?(Symbol)
- name = name.intern
- end
- if type.allowedmethod(name)
- type.send(self.name,self.args)
- else
- Blink.error("%s does not respond to %s" % [self.type,self.name])
- end
- else
- raise "Could not find object type %s" % setting.type
- end
- end
- end
- #------------------------------------------------------------
-
- #------------------------------------------------------------
- # just a linear container for objects
- class TransBucket < Array
- attr_accessor :name, :type
-
- def push(*args)
- args.each { |arg|
- case arg
- when Blink::TransBucket, Blink::TransObject, Blink::TransSetting
- # nada
- else
- raise "TransBuckets cannot handle objects of type %s" %
- arg.class
- end
- }
- super
- end
-
- def to_type
- # this container will contain the equivalent of all objects at
- # this level
- #container = Blink::Component.new(:name => @name, :type => @type)
- unless defined? @name
- raise "TransBuckets must have names"
- end
- unless defined? @type
- Blink.verbose "TransBucket '%s' has no type" % @name
- end
- hash = {
- :name => @name,
- :type => @type
- }
- if defined? @parameters
- @parameters.each { |param,value|
- Blink.warning "Defining %s on %s of type %s" %
- [param,@name,@type]
- hash[param] = value
- }
- else
- Blink.warning "%s has no parameters" % @name
- end
- container = Blink::Component.new(hash)
- nametable = {}
-
- self.each { |child|
- # the fact that we descend here means that we are
- # always going to execute depth-first
- # which is _probably_ a good thing, but one never knows...
- if child.is_a?(Blink::TransBucket)
- # just perform the same operation on any children
- container.push(child.to_type)
- elsif child.is_a?(Blink::TransSetting)
- # XXX this is wrong, but for now just evaluate the settings
- child.evaluate
- elsif child.is_a?(Blink::TransObject)
- # do a simple little naming hack to see if the object already
- # exists in our scope
- # this assumes that type/name combinations are globally
- # unique
- name = [child[:name],child.type].join("--")
-
- if nametable.include?(name)
- object = nametable[name]
- child.each { |var,value|
- # don't rename; this shouldn't be possible anyway
- next if var == :name
-
- Blink.notice "Adding %s to %s" % [var,name]
- # override any existing values
- object[var] = value
- }
- else # the object does not exist yet in our scope
- # now we have the object instantiated, in our scope
- object = child.to_type
- nametable[name] = object
-
- # this sets the order of the object
- container.push object
- end
- else
- raise "TransBucket#to_type cannot handle objects of type %s" %
- child.class
- end
- }
-
- # at this point, no objects at are level are still Transportable
- # objects
- return container
- end
-
- def param(param,value)
- unless defined? @parameters
- @parameters = {}
- end
- @parameters[param] = value
- end
-
- end
- #------------------------------------------------------------
-end
diff --git a/lib/blink/type.rb b/lib/blink/type.rb
deleted file mode 100644
index 51cd904e7..000000000
--- a/lib/blink/type.rb
+++ /dev/null
@@ -1,851 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# included so we can test object types
-require 'blink'
-require 'blink/element'
-require 'blink/event'
-require 'blink/type/state'
-
-
-# XXX see the bottom of the file for the rest of the inclusions
-
-#---------------------------------------------------------------
-# This class is the abstract base class for the mechanism for organizing
-# work. No work is actually done by this class or its subclasses; rather,
-# the subclasses include states which do the actual work.
-# See state.rb for how work is actually done.
-
-# our duck type interface -- if your object doesn't match this interface,
-# it won't work
-
-# all of our first-class objects (objects, states, and components) will
-# respond to these methods
-# although states don't inherit from Blink::Type
-# although maybe Blink::State should...
-
-# the default behaviour that this class provides is to just call a given
-# method on each contained object, e.g., in calling 'sync', we just run:
-# object.each { |subobj| subobj.sync() }
-
-# to use this interface, just define an 'each' method and 'include Blink::Type'
-
-module Blink
-class Type < Blink::Element
- attr_accessor :children, :parameters, :parent
- include Enumerable
-
- @@allobjects = Array.new # an array for all objects
- @abstract = true
-
- @name = :blink # a little fakery, since Blink itself isn't a type
- @namevar = :notused
-
- @states = []
- @parameters = [:notused]
-
- @allowedmethods = [:noop,:debug,:statefile]
-
- @@metaparams = [
- :onerror,
- :schedule,
- :check,
- :require
- ]
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # class methods dealing with Type management
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # these objects are used for mapping type names (e.g., 'file')
- # to actual object classes; because Type.inherited is
- # called before the <subclass>.name method is defined, we need
- # to store each class in an array, and then later actually iterate
- # across that array and make a map
- @@typeary = [self] # so that the allowedmethods stuff works
- @@typehash = Hash.new { |hash,key|
- if key.is_a?(String)
- key = key.intern
- end
- if hash.include?(key)
- hash[key]
- else
- raise "Object type %s not found" % key
- end
- }
-
- #---------------------------------------------------------------
- # a test for whether this type is allowed to have instances
- # on clients
- # subclasses can just set '@abstract = true' to mark themselves
- # as abstract
- def Type.abstract
- if defined? @abstract
- return @abstract
- else
- return false
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.allowedmethod(method)
- if defined? @allowedmethods and @allowedmethods.include?(method)
- return true
- else
- return false
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.statefile(file)
- Blink[:statefile] = file
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # ill thought-out
- # this needs to return @noop
- #def noop(ary)
- # Blink[:noop] = ary.shift
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def debug(ary)
- # Blink[:debug] = ary.shift
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this is meant to be run multiple times, e.g., when a new
- # type is defined at run-time
- def Type.buildtypehash
- @@typeary.each { |otype|
- if @@typehash.include?(otype.name)
- if @@typehash[otype.name] != otype
- Blink.warning("Object type %s is already defined (%s vs %s)" %
- [otype.name,@@typehash[otype.name],otype])
- end
- else
- @@typehash[otype.name] = otype
- end
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.eachtype
- @@typeary.each { |type| yield type }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this should make it so our subclasses don't have to worry about
- # defining these class instance variables
- def Type.inherited(sub)
- sub.initvars
-
- #Blink.notice("subtype %s(%s) just created" % [sub,sub.superclass])
- # add it to the master list
- # unfortunately we can't yet call sub.name, because the #inherited
- # method gets called before any commands in the class definition
- # get executed, which, um, sucks
- @@typeary.push(sub)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this is so we don't have to eval this code
- # init all of our class instance variables
- def Type.initvars
- @objects = Hash.new
- @actions = Hash.new
- #Blink.verbose "initing validstates for %s" % self
- @validstates = {}
- @validparameters = {}
-
- unless defined? @states
- @states = {}
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.metaclass
- if defined? @metaclass
- return @metaclass
- else
- return false
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this is used for mapping object types (e.g., Blink::Type::File)
- # to names (e.g., "file")
- def Type.name
- return @name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.newtype(type)
- raise "Type.newtype called, but I don't know why"
- @@typeary.push(type)
- if @@typehash.has_key?(type.name)
- Blink.notice("Redefining object type %s" % type.name)
- end
- @@typehash[type.name] = type
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.type(type)
- unless @@typeary.length == @@typehash.length
- Type.buildtypehash
- end
- @@typehash[type]
- end
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # class methods dealing with type instance management
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # retrieve a named object
- def Type.[](name)
- if @objects.has_key?(name)
- return @objects[name]
- else
- return nil
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.[]=(name,object)
- newobj = nil
- if object.is_a?(Blink::Type)
- newobj = object
- else
- raise "must pass a Blink::Type object"
- end
-
- if @objects.has_key?(newobj.name)
- raise "Object '%s' of type '%s' already exists with id '%s' vs. '%s'" %
- [newobj.name,newobj.class.name,
- @objects[newobj.name].object_id,newobj.object_id]
- else
- #Blink.debug("adding %s of type %s to class list" %
- # [object.name,object.class])
- @objects[newobj.name] = newobj
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # remove all type instances
- def Type.allclear
- @@typeary.each { |subtype|
- Blink.notice "Clearing %s of objects" % subtype
- subtype.clear
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # per-type clearance
- def Type.clear
- if defined? @objects
- @objects.clear
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # all objects total
- def Type.push(object)
- @@allobjects.push object
- #Blink.debug("adding %s of type %s to master list" %
- # [object.name,object.class])
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # some simple stuff to make it easier to get a name from everyone
- def Type.namevar
- unless defined? @namevar and ! @namevar.nil?
- raise "Class %s has no namevar defined" % self
- end
- return @namevar
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.has_key?(name)
- return @objects.has_key?(name)
- end
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # class and instance methods dealing with parameters and states
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.buildstatehash
- unless defined? @validstates
- @validstates = Hash.new(false)
- end
- @states.each { |stateklass|
- name = stateklass.name
- if @validstates.include?(name)
- if @validstates[name] != stateklass
- raise "Redefining state %s(%s) in %s" % [name,stateklass,self]
- else
- # it's already there, so don't bother
- end
- else
- @validstates[name] = stateklass
- end
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # Is the parameter in question a meta-parameter?
- def Type.metaparam(param)
- @@metaparams.include?(param)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this is probably only used by FileRecord objects
- def Type.parameters=(params)
- Blink.notice "setting parameters to [%s]" % params.join(" ")
- @parameters = params.collect { |param|
- if param.class == Symbol
- param
- else
- param.intern
- end
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.states
- return @states
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.validstates
- return @validstates
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.validstate(name)
- unless @validstates.length == @states.length
- self.buildstatehash
- end
- if @validstates.include?(name)
- return @validstates[name]
- else
- return false
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.validparameter(name)
- unless defined? @parameters
- raise "Class %s has not defined parameters" % self
- end
- return @parameters.include?(name)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def Type.validparam(name)
- self.validstate(name) or self.validparameter(name) or false
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this abstracts accessing parameters and states, and normalizes
- # access to always be symbols, not strings
- def [](name)
- mname = name
- if name.is_a?(String)
- mname = name.intern
- end
- unless self.class.validparam(name)
- raise "Invalid parameter %s" % [mname]
- end
- if @states.include?(mname)
- # if they're using [], they don't know if we're a state or a string
- # thus, return a string
- # if they want the actual state object, they should use state()
- return @states[mname].is
- elsif @parameters.include?(mname)
- return @parameters[mname]
- else
- return nil
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this abstracts setting parameters and states, and normalizes
- # access to always be symbols, not strings
- def []=(name,value)
- mname = name
- if name.is_a?(String)
- mname = name.intern
- end
-
- if Blink::Type.metaparam(mname)
- # call the metaparam method
- self.send(("meta" + mname.id2name),value)
- elsif stateklass = self.class.validstate(mname)
- if value.is_a?(Blink::State)
- Blink.debug "'%s' got handed a state for '%s'" % [self,mname]
- @states[mname] = value
- else
- if @states.include?(mname)
- @states[mname].should = value
- else
- @states[mname] = stateklass.new(
- :parent => self,
- :should => value
- )
- #Blink.notice "Adding parent to %s" % mname
- #@states[mname].parent = self
- end
- end
- elsif self.class.validparameter(mname)
- @parameters[mname] = value
- else
- raise "Invalid parameter %s" % [mname]
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # removing states
- def delete(attr)
- if @states.has_key?(attr)
- @states.delete(attr)
- else
- raise "Undefined state '#{attr}' in #{self}"
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def state(name)
- return @states[name]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def parameter(name)
- return @parameters[name]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # instance methods related to instance intrinsics
- # e.g., initialize() and name()
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def initialize(hash)
- @children = []
- @evalcount = 0
-
- @subscriptions = []
-
- # states and parameters are treated equivalently from the outside:
- # as name-value pairs (using [] and []=)
- # internally, however, parameters are merely a hash, while states
- # point to State objects
- # further, the lists of valid states and parameters are defined
- # at the class level
- @states = Hash.new(false)
- @parameters = Hash.new(false)
-
- @noop = false
-
- # which objects to notify when we change
- @notify = []
-
- # keeping stats for the total number of changes, and how many were
- # completely sync'ed
- # this isn't really sufficient either, because it adds lots of special cases
- # such as failed changes
- # it also doesn't distinguish between changes from the current transaction
- # vs. changes over the process lifetime
- @totalchanges = 0
- @syncedchanges = 0
- @failedchanges = 0
-
- hash.each { |var,value|
- unless var.is_a? Symbol
- hash[var.intern] = value
- hash.delete(var)
- end
- }
-
- if hash.include?(:noop)
- @noop = hash[:noop]
- hash.delete(:noop)
- end
-
- # we have to set the name of our object before anything else,
- # because it might be used in creating the other states
- namevar = self.class.namevar
-
- # if they're not using :name for the namevar but we got :name (probably
- # from the parser)
- if namevar != :name and hash.include?(:name) and ! hash[:name].nil?
- self[namevar] = hash[:name]
- hash.delete(:name)
- # else if we got the namevar
- elsif hash.has_key?(namevar) and ! hash[namevar].nil?
- self[namevar] = hash[namevar]
- hash.delete(namevar)
- # else something's screwy
- else
- p hash
- p namevar
- raise TypeError.new("A name must be provided to %s at initialization time" %
- self.class)
- end
-
- hash.each { |param,value|
- #Blink.debug("adding param '%s' with value '%s'" %
- # [param,value])
- self[param] = value
- }
-
- # add this object to the specific class's list of objects
- #Blink.notice("Adding [%s] to %s" % [self.name,self.class])
- self.class[self.name] = self
-
- # and then add it to the master list
- Blink::Type.push(self)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # return the full path to us, for logging and rollback
- # some classes (e.g., FileTypeRecords) will have to override this
- def fqpath
- return self.class, self.name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this might result from a state or from a parameter
- def name
- return self[self.class.namevar]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def retrieve
- # it's important to use the method here, so we always get
- # them back in the right order
- self.states.collect { |state|
- state.retrieve
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def sync
- self.collect { |child|
- child.sync
- }.reject { |event|
- ! (event.is_a?(Symbol) or event.is_a?(String))
- }.flatten
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def to_s
- self.name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # instance methods dealing with contained states
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def states
- Blink.debug "%s has %s states" % [self,@states.length]
- tmpstates = []
- self.class.states.each { |state|
- if @states.include?(state.name)
- tmpstates.push(@states[state.name])
- end
- }
- unless tmpstates.length == @states.length
- raise "Something went very wrong with tmpstates creation"
- end
- return tmpstates
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def eachstate
- self.states.each { |state|
- yield state
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # iterate across all children, and then iterate across states
- # we do children first so we're sure that all dependent objects
- # are checked first
- # we ignore parameters here, because they only modify how work gets
- # done, they don't ever actually result in work specifically
- def each
- # we want to return the states in the order that each type
- # specifies it, because it may (as in the case of File#create)
- # be important
- @children.each { |child|
- yield child
- }
- self.eachstate { |state|
- yield state
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def push(*child)
- @children.push(*child)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # instance methods dealing with actually doing work
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def newchange
- @totalchanges += 1
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this method is responsible for collecting state changes
- # we always descend into the children before we evaluate our current
- # states
- # this returns any changes resulting from testing, thus 'collect'
- # rather than 'each'
- def evaluate
- unless defined? @evalcount
- Blink.error "No evalcount defined on '%s' of type '%s'" %
- [self.name,self.class]
- end
- # if we're a metaclass and we've already evaluated once...
- if self.metaclass and @evalcount > 0
- return
- end
- @evalcount += 1
-
- changes = @children.collect { |child|
- child.evaluate
- }
-
- # this only operates on states, not states + children
- self.retrieve
- unless self.insync?
- changes << self.states.find_all { |state|
- ! state.insync?
- }.collect { |state|
- Blink::StateChange.new(state)
- }
- end
- # collect changes and return them
- # these changes could be from child objects or from contained states
- #self.collect { |child|
- # child.evaluate
- #}
- return changes
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # if all contained objects are in sync, then we're in sync
- def insync?
- insync = true
-
- self.states.each { |state|
- unless state.insync?
- Blink.debug("%s is not in sync" % state)
- insync = false
- end
- }
-
- Blink.debug("%s sync status is %s" % [self,insync])
- return insync
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # do we actually do work, or do we modify the system instead?
- # instances of a metaclass only get executed once per client process,
- # while instances of normal classes get run every time
- def metaclass
- return self.class.metaclass
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #---------------------------------------------------------------
- # Meta-parameter methods: These methods deal with the results
- # of specifying metaparameters
- #---------------------------------------------------------------
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this just marks states that we definitely want to retrieve values
- # on
- def metacheck(args)
- unless args.is_a?(Array)
- args = [args]
- end
-
- # these are states that we might not have values for but we want to retrieve
- # values for anyway
- args.each { |state|
- unless state.is_a?(Symbol)
- state = state.intern
- end
- next if @states.include?(state)
-
- stateklass = nil
- unless stateklass = self.class.validstate(state)
- raise "%s is not a valid state for %s" % [state,self.class]
- end
-
- # XXX it's probably a bad idea to have code this important in
- # two places
- @states[state] = stateklass.new(
- :parent => self
- )
- #@states[state] = stateklass.new()
- #@states[state].parent = self
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def subscribe(hash)
- if hash[:event] == '*'
- hash[:event] = :ALL_EVENTS
- end
-
- hash[:source] = self
- sub = Blink::Event::Subscription.new(hash)
-
- # add to the correct area
- @subscriptions.push sub
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # return all of the subscriptions to a given event
- def subscribers?(event)
- @subscriptions.find_all { |sub|
- sub.event == event.event or
- sub.event == :ALL_EVENTS
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # for each object we require, subscribe to all events that it
- # generates
- # we might reduce the level of subscription eventually, but for now...
- def metarequire(requires)
- unless requires.is_a?(Array)
- requires = [requires]
- end
- requires.each { |rname|
- # we just have a name and a type, and we need to convert it
- # to an object...
- type = nil
- object = nil
- tname = rname[0]
- unless type = Blink::Type.type(tname)
- raise "Could not find type %s" % tname
- end
- name = rname[1]
- unless object = type[name]
- raise "Could not retrieve object '%s' of type '%s'" %
- [name,type]
- end
- Blink.debug("%s requires %s" % [self.name,object])
-
- # for now, we only support this one method, 'refresh'
- object.subscribe(
- :event => '*',
- :target => self,
- :method => :refresh
- )
- #object.addnotify(self)
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def metaonerror(response)
- Blink.debug("Would have called metaonerror")
- @onerror = response
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def metaschedule(schedule)
- @schedule = schedule
- end
- #---------------------------------------------------------------
-end # Blink::Type
-end
-
-require 'blink/type/service'
-require 'blink/type/file'
-require 'blink/type/symlink'
-require 'blink/type/package'
-require 'blink/type/component'
-require 'blink/statechange'
-#require 'blink/type/typegen'
-#require 'blink/type/typegen/filetype'
-#require 'blink/type/typegen/filerecord'
diff --git a/lib/blink/type/component.rb b/lib/blink/type/component.rb
deleted file mode 100644
index 38bf3326d..000000000
--- a/lib/blink/type/component.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# the object allowing us to build complex structures
-# this thing contains everything else, including itself
-
-require 'blink'
-require 'blink/type'
-require 'blink/transaction'
-
-module Blink
- class Component < Blink::Type
- include Enumerable
-
- @name = :component
- @namevar = :name
-
- @states = []
- @parameters = [:name,:type]
-
- def each
- @children.each { |child| yield child }
- end
-
- def initialize(args)
- @children = []
- super(args)
- Blink.verbose "Made component with name %s" % self.name
- end
-
- # now we decide whether a transaction is dumb, and just accepts
- # changes from the container, or whether it collects them itself
- # for now, because i've already got this implemented, let transactions
- # collect the changes themselves
- def evaluate
- transaction = Blink::Transaction.new(@children)
- transaction.component = self
- return transaction
- end
-
- def push(*ary)
- ary.each { |child|
- unless child.is_a?(Blink::Element)
- Blink.notice "Got object of type %s" % child.class
- raise "Containers can only contain Blink::Elements"
- end
- @children.push child
- }
- end
-
- def name
- return "%s[%s]" % [@parameters[:type],@parameters[:name]]
- end
-
- def refresh
- @children.collect { |child|
- if child.respond_to?(:refresh)
- child.refresh
- end
- }
- end
-
- def retrieve
- self.collect { |child|
- child.retrieve
- }
- end
-
- def to_s
- return "component(%s)" % self.name
- end
- end
-end
diff --git a/lib/blink/type/file.rb b/lib/blink/type/file.rb
deleted file mode 100644
index 9d0f98c61..000000000
--- a/lib/blink/type/file.rb
+++ /dev/null
@@ -1,450 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'digest/md5'
-require 'etc'
-require 'blink/type/state'
-
-module Blink
- # we first define all of the state that our file will use
- # because the objects must be defined for us to use them in our
- # definition of the file object
- class State
- class FileCreate < Blink::State
- require 'etc'
- attr_accessor :file
- @name = :create
- @event = :file_created
-
- def should=(value)
- # default to just about anything meaning 'true'
- if value == false or value.nil?
- @should = false
- else
- @should = true
- end
- end
-
- def retrieve
- stat = nil
-
- self.is = FileTest.exist?(self.parent[:path])
- Blink.debug "'exists' state is %s" % self.is
- end
-
-
- def sync
- begin
- File.open(self.parent[:path],"w") { # just create an empty file
- }
- rescue => detail
- raise detail
- end
- return :file_created
- end
- end
-
- class FileChecksum < Blink::State
- @name = :checksum
- @event = :file_modified
-
- def should=(value)
- @checktype = value
- state = Blink::Storage.state(self)
- if hash = state[self.parent[:path]]
- if hash.include?(@checktype)
- @should = hash[@checktype]
- else
- Blink.verbose "Found checksum for %s but not of type %s" %
- [self.parent[:path],@checktype]
- @should = nil
- end
- else
- Blink.debug "No checksum for %s" % self.parent[:path]
- end
- end
-
- def retrieve
- unless defined? @checktype
- @checktype = "md5"
- end
-
- sum = ""
- case @checktype
- when "md5":
- File.open(self.parent[:path]) { |file|
- sum = Digest::MD5.hexdigest(file.read)
- }
- when "md5lite":
- File.open(self.parent[:path]) { |file|
- sum = Digest::MD5.hexdigest(file.read(512))
- }
- when "timestamp","mtime":
- sum = File.stat(self.parent[:path]).mtime
- when "time":
- sum = File.stat(self.parent[:path]).ctime
- end
-
- self.is = sum
-
- Blink.debug "checksum state is %s" % self.is
- end
-
-
- # at this point, we don't actually modify the system, we just kick
- # off an event if we detect a change
- def sync
- if self.updatesum
- return :file_modified
- else
- return nil
- end
- end
-
- def updatesum
- state = Blink::Storage.state(self)
- unless state.include?(self.parent[:path])
- state[self.parent[:path]] = Hash.new
- end
- # if we're replacing, vs. updating
- if state[self.parent[:path]].include?(@checktype)
- Blink.debug "Replacing checksum %s with %s" %
- [state[self.parent[:path]][@checktype],@is]
- result = true
- else
- Blink.verbose "Creating checksum %s for %s of type %s" %
- [@is,self.parent[:path],@checktype]
- result = false
- end
- state[self.parent[:path]][@checktype] = @is
- return result
- end
- end
-
- class FileUID < Blink::State
- require 'etc'
- attr_accessor :file
- @name = :owner
- @event = :inode_changed
-
- def retrieve
- stat = self.parent.stat(true)
-
- self.is = stat.uid
- if defined? @should
- unless @should.is_a?(Integer)
- begin
- user = Etc.getpwnam(@should)
- if user.gid == ""
- raise "Could not retrieve uid for '%s'" % self.parent
- end
- Blink.debug "converting %s to integer '%d'" %
- [@should,user.uid]
- @should = user.uid
- rescue
- raise "Could not get any info on user '%s'" % @should
- end
- end
- end
-
- Blink.debug "chown state is %d" % self.is
- end
-
- def sync
- if @is == -1
- self.parent.stat(true)
- self.retrieve
- Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is]
- end
-
- unless self.parent.stat
- Blink.error "File '%s' does not exist; cannot chown" %
- self.parent[:path]
- end
-
- begin
- File.chown(self.should,-1,self.parent[:path])
- rescue => detail
- raise "failed to chown '%s' to '%s': %s" %
- [self.parent[:path],self.should,detail]
- end
-
- return :inode_changed
- end
- end
-
- # this state should actually somehow turn into many states,
- # one for each bit in the mode
- # I think MetaStates are the answer, but I'm not quite sure
- class FileMode < Blink::State
- require 'etc'
-
- @name = :mode
- @event = :inode_changed
-
- def should=(should)
- # this is pretty hackish, but i need to make sure the number is in
- # octal, yet the number can only be specified as a string right now
- unless should.is_a?(Integer) # i've already converted it correctly
- unless should =~ /^0/
- should = "0" + should
- end
- should = Integer(should)
- end
- @should = should
- end
-
- def retrieve
- stat = self.parent.stat(true)
- self.is = stat.mode & 007777
-
- Blink.debug "chmod state is %o" % self.is
- end
-
- def sync
- if @is == -1
- self.parent.stat(true)
- self.retrieve
- Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is]
- end
-
- unless self.parent.stat
- Blink.error "File '%s' does not exist; cannot chmod" %
- self.parent[:path]
- return
- end
-
- begin
- File.chmod(self.should,self.parent[:path])
- rescue
- raise "failed to chmod #{self.parent[:path]}: #{$!}"
- end
- return :inode_changed
- end
- end
-
- # not used until I can figure out how to solve the problem with
- # metastates
- class FileSetUID < Blink::State
- require 'etc'
-
- @parent = Blink::State::FileMode
-
- @name = :setuid
- @event = :inode_changed
-
- def <=>(other)
- self.is <=> @parent.value[11]
- end
-
- # this just doesn't seem right...
- def sync
- unless defined? @is or @is == -1
- self.parent.stat(true)
- self.retrieve
- Blink.notice "%s: should is '%s'" % [self.class.name,self.should]
- end
- tmp = 0
- if self.is == true
- tmp = 1
- end
- @parent.value[11] = tmp
- return :inode_changed
- end
- end
-
- class FileGroup < Blink::State
- require 'etc'
-
- @name = :group
- @event = :inode_changed
-
- def retrieve
- stat = self.parent.stat(true)
-
- self.is = stat.gid
-
- # we probably shouldn't actually modify the 'should' value
- # but i don't see a good way around it right now
- # mmmm, should
- if defined? @should
- unless self.should.is_a?(Integer)
- begin
- require 'blink/fact'
- group = Etc.getgrnam(self.should)
- # apparently os x is six shades of weird
- os = Blink::Fact["Operatingsystem"]
-
- gid = ""
- case os
- when "Darwin":
- gid = group.passwd
- else
- gid = group.gid
- end
- if gid == ""
- raise "Could not retrieve gid for %s" % self.parent
- end
- Blink.debug "converting %s to integer %d" %
- [self.should,gid]
- self.should = gid
- rescue
- #raise "Could not get any info on group %s" % self.should
- raise
- end
- end
- end
- Blink.debug "chgrp state is %d" % self.is
- end
-
- def sync
- Blink.debug "setting chgrp state to %s" % self.should
- if @is == -1
- self.parent.stat(true)
- self.retrieve
- Blink.notice "%s: after refresh, is '%s'" % [self.class.name,@is]
- end
-
- unless self.parent.stat
- Blink.error "File '%s' does not exist; cannot chgrp" %
- self.parent[:path]
- return
- end
-
- begin
- # set owner to nil so it's ignored
- File.chown(nil,self.should,self.parent[:path])
- rescue
- raise "failed to chgrp %s to %s: %s" %
- [self.parent[:path], self.should, $!]
- end
- return :inode_changed
- end
- end
- end
- class Type
- class File < Type
- attr_reader :params
- # class instance variable
- @states = [
- Blink::State::FileCreate,
- Blink::State::FileUID,
- Blink::State::FileGroup,
- Blink::State::FileMode,
- Blink::State::FileChecksum,
- Blink::State::FileSetUID
- ]
-
- @parameters = [
- :path,
- :recurse
- ]
-
- @name = :file
- @namevar = :path
-
- # a wrapper method to make sure the file exists before doing anything
- def retrieve
- unless stat = self.stat(true)
- Blink.verbose "File %s does not exist" % self[:path]
- @states.each { |name,state|
- state.is = -1
- }
- return
- end
- super
- end
-
- def stat(refresh = false)
- if @stat.nil? or refresh == true
- begin
- @stat = ::File.stat(self[:path])
- rescue => error
- Blink.debug "Failed to stat %s: %s" %
- [self[:path],error]
- @stat = nil
- end
- end
-
- return @stat
- end
-
- def initialize(hash)
- arghash = hash.dup
- super
- @stat = nil
-
- # if recursion is enabled and we're a directory...
- if @parameters[:recurse] and self.stat.directory?
- recurse = self[:recurse]
- # we might have a string, rather than a number
- if recurse.is_a?(String)
- if recurse =~ /^[0-9]+$/
- recurse = Integer(recurse)
- elsif recurse =~ /^inf/ # infinite recursion
- recurse = true
- end
- end
-
- # unless we're at the end of the recursion
- if recurse != 0
- arghash.delete("recurse")
- if recurse.is_a?(Integer)
- recurse -= 1 # reduce the level of recursion
- end
-
- arghash[:recurse] = recurse
-
- # now make each contained file/dir a child
- unless defined? @children
- @children = []
- end
-
- Dir.foreach(self[:path]) { |file|
- next if file =~ /^\.\.?/ # skip . and ..
-
- arghash[:path] = ::File.join(self[:path],file)
-
- child = nil
- # if the file already exists...
- if child = self.class[arghash[:path]]
- arghash.each { |var,value|
- next if var == :path
- child[var] = value
- }
- else # create it anew
- child = self.class.new(arghash)
- end
- @children.push child
- }
- end
- end
- end
- end # Blink::Type::File
- end # Blink::Type
-
- class FileSource
- attr_accessor :name
-
- @sources = Hash.new(nil)
-
- def FileSource.[]=(name,sub)
- @sources[name] = sub
- end
-
- def FileSource.[](name)
- return @sources[name]
- end
-
- def initialize(name)
- @name = name
-
- if block_given?
- yield self
- end
-
- FileSource[name] = self
- end
- end
-end
diff --git a/lib/blink/type/package.rb b/lib/blink/type/package.rb
deleted file mode 100644
index 77bcd76b2..000000000
--- a/lib/blink/type/package.rb
+++ /dev/null
@@ -1,366 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'blink/type/state'
-require 'blink/fact'
-
-module Blink
- class State
- class PackageInstalled < Blink::State
- @name = :install
-
- def retrieve
- #self.is = Blink::PackageTyping[@object.format][@object.name]
- unless @parent.class.listed
- @parent.class.getpkglist
- end
- Blink.debug "package install state is %s" % self.is
- end
-
- def sync
- #begin
- raise "cannot sync package states yet"
- #rescue
- # raise "failed to sync #{@params[:file]}: #{$!}"
- #end
-
- #return :package_installed
- end
- end
- end
-
- class Type
- # packages are complicated because each package format has completely
- # different commands. We need some way to convert specific packages
- # into the general package object...
- class Package < Type
- attr_reader :version, :format
- @states = [
- Blink::State::PackageInstalled
- ]
- @parameters = [
- :format,
- :name,
- :status,
- :version,
- :category,
- :platform,
- :root,
- :vendor,
- :description
- ]
-
- @name = :package
- @namevar = :name
- @listed = false
-
- @allowedmethods = [:types]
- @@types = nil
-
- def Package.clear
- @listed = false
- super
- end
-
- def Package.listed
- return @listed
- end
-
- def Package.types(array)
- unless array.is_a?(Array)
- array = [array]
- end
- @@types = array
- Blink.warning "Types are %s" % array.join(" ")
- end
-
- def Package.getpkglist
- if @@types.nil?
- case Blink::Fact["operatingsystem"]
- when "SunOS": @@types = ["sunpkg"]
- when "Linux":
- case Blink::Fact["distro"]
- when "Debian": @@types = ["dpkg"]
- else
- raise "No default type for " + Blink::Fact["distro"]
- end
- else
- raise "No default type for " + Blink::Fact["operatingsystem"]
- end
- end
-
- list = @@types.collect { |type|
- if typeobj = Blink::PackagingType[type]
- # pull all of the objects
- typeobj.list
- else
- raise "Could not find package type '%s'" % type
- end
- }.flatten
- @listed = true
- return list
- end
-
- def Package.installedpkg(hash)
- # this is from code, so we don't have to do as much checking
- name = hash[:name]
-
- # if it already exists, modify the existing one
- if object = Package[name]
- states = {}
- object.states.each { |state|
- Blink.warning "Adding %s" % state.name.inspect
- states[state.name] = state
- }
- hash.each { |var,value|
- if states.include?(var)
- Blink.verbose "%s is a set state" % var.inspect
- states[var].is = value
- else
- Blink.verbose "%s is not a set state" % var.inspect
- if object[var] and object[var] != value
- Blink.warning "Overriding %s => %s on %s with %s" %
- [var,object[var],name,value]
- end
-
- object[var] = value
-
- # swap the values if we're a state
- if states.include?(var)
- Blink.verbose "Swapping %s because it's a state" % var
- states[var].is = value
- states[var].should = nil
- else
- Blink.verbose "%s is not a state" % var.inspect
- Blink.verbose "States are %s" % states.keys.collect { |st|
- st.inspect
- }.join(" ")
- end
- end
- }
- return object
- else # just create it
- return self.new(hash)
- end
- end
-
- # okay, there are two ways that a package could be created...
- # either through the language, in which case the hash's values should
- # be set in 'should', or through comparing against the system, in which
- # case the hash's values should be set in 'is'
- def initialize(hash)
- super
- end
-
- end # Blink::Type::Package
- end
-
- class PackagingType
- attr_writer :list, :install, :remove, :check
-
- @@types = Hash.new(false)
-
- def PackagingType.[](name)
- if @@types.include?(name)
- return @@types[name]
- else
- Blink.warning name.inspect
- Blink.warning @@types.keys.collect { |key|
- key.inspect
- }.join(" ")
- return nil
- end
- end
-
- # whether a package is installed or not
- def [](name)
- return @packages[name]
- end
-
- [:list, :install, :remove, :check].each { |method|
- self.send(:define_method, method) {
- # retrieve the variable
- var = eval("@" + method.id2name)
- if var.is_a?(Proc)
- var.call()
- else
- raise "only blocks are supported right now"
- end
- }
- }
-
- def initialize(name)
- if block_given?
- yield self
- end
-
- @packages = Hash.new(false)
- @@types[name] = self
- end
-
- def retrieve
- @packages.clear()
-
- @packages = self.list()
- end
- end
-
- PackagingType.new("dpkg") { |type|
- type.list = proc {
- packages = []
-
- # dpkg only prints as many columns as you have available
- # which means we don't get all of the info
- # stupid stupid
- oldcol = ENV["COLUMNS"]
- ENV["COLUMNS"] = "500"
-
- # list out all of the packages
- open("| dpkg -l") { |process|
- # our regex for matching dpkg output
- regex = %r{^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$}
- fields = [:status, :name, :install, :description]
- hash = {}
-
- 5.times { process.gets } # throw away the header
-
- # now turn each returned line into a package object
- process.each { |line|
- if match = regex.match(line)
- hash.clear
-
- fields.zip(match.captures) { |field,value|
- hash[field] = value
- }
- packages.push Blink::Type::Package.installedpkg(hash)
- else
- raise "failed to match dpkg line %s" % line
- end
- }
- }
- ENV["COLUMNS"] = oldcol
-
- return packages
- }
-
- # we need package retrieval mechanisms before we can have package
- # installation mechanisms...
- #type.install = proc { |pkg|
- # raise "installation not implemented yet"
- #}
-
- type.remove = proc { |pkg|
- cmd = "dpkg -r %s" % pkg.name
- output = %x{#{cmd}}
- if $? != 0
- raise output
- end
- }
- }
-
- PackagingType.new("sunpkg") { |type|
- type.list = proc {
- packages = []
- hash = {}
- names = {
- "PKGINST" => :name,
- "NAME" => nil,
- "CATEGORY" => :category,
- "ARCH" => :platform,
- "VERSION" => :install,
- "BASEDIR" => :root,
- "HOTLINE" => nil,
- "EMAIL" => nil,
- "VENDOR" => :vendor,
- "DESC" => :description,
- "PSTAMP" => nil,
- "INSTDATE" => nil,
- "STATUS" => nil,
- "FILES" => nil
- }
-
- # list out all of the packages
- open("| pkginfo -l") { |process|
- # we're using the long listing, so each line is a separate piece
- # of information
- process.each { |line|
- case line
- when /^$/ then
- packages.push Blink::Type::Package.installedpkg(hash)
- hash.clear
- when /\s*(\w+):\s+(.+)/
- name = $1
- value = $2
- if names.include?(name)
- hash[names[name]] = value
- else
- raise "Could not find %s" % name
- end
- when /\s+\d+.+/
- # nothing; we're ignoring the FILES info
- end
- }
- }
- return packages
- }
-
- # we need package retrieval mechanisms before we can have package
- # installation mechanisms...
- #type.install = proc { |pkg|
- # raise "installation not implemented yet"
- #}
-
- type.remove = proc { |pkg|
- cmd = "pkgrm -n %s" % pkg.name
- output = %x{#{cmd}}
- if $? != 0
- raise output
- end
- }
- }
-
- # this is how we retrieve packages
- class PackageSource
- attr_accessor :uri
- attr_writer :retrieve
-
- @@sources = Hash.new(false)
-
- def PackageSource.get(file)
- type = file.sub(%r{:.+},'')
- source = nil
- if source = @@sources[type]
- return source.retrieve(file)
- else
- raise "Unknown package source: %s" % type
- end
- end
-
- def initialize(name)
- if block_given?
- yield self
- end
-
- @@sources[name] = self
- end
-
- def retrieve(path)
- @retrieve.call(path)
- end
-
- end
-
- PackageSource.new("file") { |obj|
- obj.retrieve = proc { |path|
- # this might not work for windows...
- file = path.sub(%r{^file://},'')
-
- if FileTest.exists?(file)
- return file
- else
- raise "File %s does not exist" % file
- end
- }
- }
-end
diff --git a/lib/blink/type/process.rb b/lib/blink/type/process.rb
deleted file mode 100644
index b42f75f26..000000000
--- a/lib/blink/type/process.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# DISABLED
-# I'm only working on services, not processes, right now
-
-module Blink
- class State
- class ProcessRunning < State
- def retrieve
- running = 0
- regex = Regexp.new(@params[:pattern])
- begin
- # this ps is only tested on Solaris
- # XXX yeah, this definitely needs to be fixed...
- %x{ps -ef -U #{@params[:user]}}.split("\n").each { |process|
- if regex.match(process)
- running += 1
- end
- }
- rescue
- # this isn't correct, but what the hell
- Blink::Message.new(
- :level => :error,
- :source => self.parent,
- :message => "Failed to run ps"
- )
- end
-
- self.state = running
- Blink.debug "there are #{running} #{self.parent} processes for start"
- end
-
- def <=>(other)
- self.state < 1
- end
-
- def fix
- require 'etc'
- # ruby is really cool
- uid = 0
- if @params[:user].is_a? Integer
- uid = @params[:user]
- else
- uid = Etc.getpwnam(@params[:user]).uid
- end
- Kernel.fork {
- Process.uid = uid
- Process.euid = uid
- string = @params[:binary] + (@params[:arguments] || "")
- Blink::Message.new(
- :level => :notice,
- :source => self.parent,
- :message => "starting"
- )
- Kernel.exec(string)
- }
- end
- end
- end
- class Type
- class BProcess < Type
- attr_reader :stat, :path
- @parameters = [:start, :stop, :user, :pattern, :binary, :arguments]
- @name = :process
-
- @namevar = :pattern
-
- Blink::Relation.new(self, Blink::Operation::Start, {
- :user => :user,
- :pattern => :pattern,
- :binary => :binary,
- :arguments => :arguments
- })
-
- Blink::Relation.new(self, Blink::Operation::Stop, {
- :user => :user,
- :pattern => :pattern
- })
-
- end # Blink::Type::BProcess
- end # Blink::Type
-
-end
diff --git a/lib/blink/type/service.rb b/lib/blink/type/service.rb
deleted file mode 100644
index f17f17e3f..000000000
--- a/lib/blink/type/service.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# this is our main way of managing processes right now
-#
-# a service is distinct from a process in that services
-# can only be managed through the interface of an init script
-# which is why they have a search path for initscripts and such
-
-module Blink
- class State
- class ServiceRunning < State
- @name = :running
- #@event = :file_created
-
- # this whole thing is annoying
- # i should probably just be using booleans, but for now, i'm not...
- def should=(should)
- case should
- when false,0,"0":
- should = 0
- when true,1,"1":
- should = 1
- else
- Blink.warning "%s: interpreting '%s' as false" %
- [self.class,should]
- should = 0
- end
- @should = should
- end
-
- def retrieve
- self.is = self.running()
- Blink.debug "Running value for '%s' is '%s'" %
- [self.parent.name,self.is]
- end
-
- # should i cache this info?
- def running
- begin
- status = self.parent.initcmd("status")
- Blink.debug "initcmd status for '%s' is '%s'" %
- [self.parent.name,status]
-
- if status # the command succeeded
- return 1
- else
- return 0
- end
- rescue SystemCallError
- raise "Could not execute %s" % initscript
- end
-
- end
-
- def sync
- if self.running > 0
- status = 1
- else
- status = 0
- end
- Blink.debug "'%s' status is '%s' and should be '%s'" %
- [self,status,should]
- event = nil
- if self.should > 0
- if status < 1
- Blink.debug "Starting '%s'" % self
- if self.parent.initcmd("start")
- event = :service_started
- else
- raise "Failed to start '%s'" % self.parent.name
- end
- else
- Blink.debug "'%s' is already running, yo" % self
- #Blink.debug "Starting '%s'" % self
- #unless self.parent.initcmd("start")
- # raise "Failed to start %s" % self.name
- #end
- end
- elsif status > 0
- Blink.debug "Stopping '%s'" % self
- if self.parent.initcmd("stop")
- event = :service_stopped
- else
- raise "Failed to stop %s" % self.name
- end
- else
- Blink.debug "Not running '%s' and shouldn't be running" % self
- end
-
- return event
- end
- end
- end
- class Type
- class Service < Type
- attr_reader :stat
- @states = [
- Blink::State::ServiceRunning
- ]
- @parameters = [
- :name,
- :pattern
- ]
-
- @functions = [
- :setpath
- ]
-
- @name = :service
- @namevar = :name
-
- @searchpaths = Array.new
- @allowedmethods = [:setpath]
-
- def Service.search(name)
- @searchpaths.each { |path|
- # must specify that we want the top-level File, not Blink::...::File
- fqname = ::File.join(path,name)
- begin
- stat = ::File.stat(fqname)
- rescue
- # should probably rescue specific errors...
- Blink.debug("Could not find %s in %s" % [name,path])
- next
- end
-
- # if we've gotten this far, we found a valid script
- return fqname
- }
- raise "Could not find init script for '%s'" % name
- end
-
- def Service.setpath(ary)
- # verify each of the paths exists
- #ary.flatten!
- @searchpaths = ary.find_all { |dir|
- retvalue = false
- begin
- retvalue = ::File.stat(dir).directory?
- rescue => detail
- Blink.verbose("Directory %s does not exist: %s" % [dir,detail])
- # just ignore it
- end
- # disallow relative paths
- #if dir !~ /^\//
- # retvalue = false
- #end
- retvalue
- }
- end
-
- # it'd be nice if i didn't throw the output away...
- # this command returns true if the exit code is 0, and returns
- # false otherwise
- def initcmd(cmd)
- script = self.initscript
-
- Blink.debug "Executing '%s %s' as initcmd for '%s'" %
- [script,cmd,self]
-
- rvalue = Kernel.system("%s %s" %
- [script,cmd])
-
- Blink.debug "'%s' ran with exit status '%s'" %
- [cmd,rvalue]
-
-
- rvalue
- end
-
- def initscript
- if defined? @initscript
- return @initscript
- else
- @initscript = Service.search(self.name)
- end
- end
-
- def refresh
- self.initcmd("restart")
- end
- end # Blink::Type::Service
- end # Blink::Type
-end
diff --git a/lib/blink/type/state.rb b/lib/blink/type/state.rb
deleted file mode 100644
index 4706b04b7..000000000
--- a/lib/blink/type/state.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'blink'
-require 'blink/element'
-require 'blink/statechange'
-
-#---------------------------------------------------------------
-# this is a virtual base class for states
-# states are self-contained building blocks for objects
-
-# States can currently only be used for comparing a virtual "should" value
-# against the real state of the system. For instance, you could verify that
-# a file's owner is what you want, but you could not create two file objects
-# and use these methods to verify that they have the same owner
-module Blink
-class State < Blink::Element
- attr_accessor :is, :should, :parent
-
- @virtual = true
-
- #---------------------------------------------------------------
- # every state class must tell us what its name will be (as a symbol)
- # this determines how we will refer to the state during usage
- # e.g., the Owner state for Files might say its name is :owner;
- # this means that we can say "file[:owner] = 'yayness'"
- def State.name
- return @name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # which event gets generated if this state change happens
- def State.generates
- return @event
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # if we're not in sync, return a statechange capable of putting us
- # in sync
- def evaluate
- Blink.verbose "evaluating %s" % self
- self.retrieve
- if self.insync?
- Blink.verbose "%s is in sync" % self
- return nil
- else
- return Blink::StateChange.new(self)
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # return the full path to us, for logging and rollback
- def fqpath
- return @parent.fqpath, self.name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # we aren't actually comparing the states themselves, we're only
- # comparing the "should" value with the "is" value
- def insync?
- Blink.debug "%s value is '%s', should be '%s'" %
- [self,self.is.inspect,self.should.inspect]
- self.is == self.should
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def initialize(hash)
- @is = nil
-
- unless hash.include?(:parent)
- raise "State %s was not passed a parent" % self
- end
- @parent = hash[:parent]
-
- if hash.include?(:should)
- self.should = hash[:should]
- else # we got passed no argument
- # leave @should undefined
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # for testing whether we should actually do anything
- def noop
- unless defined? @noop
- @noop = false
- end
- tmp = @noop || self.parent.noop || Blink[:noop] || false
- Blink.notice "noop is %s" % tmp
- return tmp
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def refresh(transaction)
- # self.retrieve
-
- # we definitely need some way to batch these refreshes, so a
- # given object doesn't get refreshed multiple times in a single
- # run
- # @parent.refresh
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # each state class must define the name() method, and state instances
- # do not change that name
- # this implicitly means that a given object can only have one state
- # instance of a given state class
- def name
- return self.class.name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # retrieve the current state from the running system
- def retrieve
- raise "'retrieve' method was not overridden by %s" % self.class
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def to_s
- return "%s(%s)" % [@parent.name,self.name]
- end
- #---------------------------------------------------------------
-end
-end
diff --git a/lib/blink/type/symlink.rb b/lib/blink/type/symlink.rb
deleted file mode 100644
index 772b5a831..000000000
--- a/lib/blink/type/symlink.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-require 'etc'
-require 'blink/type/state'
-require 'blink/type/file'
-
-module Blink
- # okay, how do we deal with parameters that don't have operations
- # associated with them?
- class State
- class SymlinkTarget < Blink::State
- require 'etc'
- attr_accessor :file
-
- @name = :target
-
- def create
- begin
- Blink.debug("Creating symlink '%s' to '%s'" %
- [self.parent[:path],self.should])
- unless File.symlink(self.should,self.parent[:path])
- raise TypeError.new("Could not create symlink '%s'" %
- self.parent[:path])
- end
- rescue => detail
- raise TypeError.new("Cannot create symlink '%s': %s" %
- [self.parent[:path],detail])
- end
- end
-
- def remove
- if FileTest.symlink?(self.parent[:path])
- Blink.debug("Removing symlink '%s'" % self.parent[:path])
- begin
- File.unlink(self.parent[:path])
- rescue
- raise TypeError.new("Failed to remove symlink '%s'" %
- self.parent[:path])
- end
- elsif FileTest.exists?(self.parent[:path])
- raise TypeError.new("Cannot remove normal file '%s'" %
- self.parent[:path])
- else
- Blink.debug("Symlink '%s' does not exist" %
- self.parent[:path])
- end
- end
-
- def retrieve
- stat = nil
-
- if FileTest.symlink?(self.parent[:path])
- self.is = File.readlink(self.parent[:path])
- Blink.debug("link value is '%s'" % self.is)
- return
- else
- self.is = nil
- return
- end
- end
-
- # this is somewhat complicated, because it could exist and be
- # a file
- def sync
- if self.should.nil?
- self.remove()
- else # it should exist and be a symlink
- if FileTest.symlink?(self.parent[:path])
- path = File.readlink(self.parent[:path])
- if path != self.should
- self.remove()
- self.create()
- end
- elsif FileTest.exists?(self.parent[:path])
- raise TypeError.new("Cannot replace normal file '%s'" %
- self.parent[:path])
- else
- self.create()
- end
- end
-
- #self.parent.newevent(:event => :inode_changed)
- end
- end
- end
-
- class Type
- class Symlink < Type
- attr_reader :stat, :path, :params
- # class instance variable
- @states = [
- Blink::State::FileUID,
- Blink::State::FileGroup,
- Blink::State::FileMode,
- Blink::State::SymlinkTarget
- ]
-
- @parameters = [
- :path
- ]
-
- @name = :symlink
- @namevar = :path
- end # Blink::Type::Symlink
- end # Blink::Type
-
-end
diff --git a/lib/blink/type/typegen.rb b/lib/blink/type/typegen.rb
deleted file mode 100644
index 85f04912c..000000000
--- a/lib/blink/type/typegen.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# parse and write configuration files using objects with minimal parsing abilities
-
-require 'etc'
-require 'blink/type'
-
-module Blink
- class Type
-class TypeGenerator < Blink::Type
- include Enumerable
-
- @namevar = :name
- @name = :typegen
- @abstract = true
-
- @parameters = [:name]
- @states = []
-
- #---------------------------------------------------------------
- def TypeGenerator.[](name)
- return @subclasses[name]
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.inherited(subclass)
- #subclass.initvars
- super(subclass)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # we don't need to 'super' here because type.rb already runs initvars
- # in Type#inherited
- def TypeGenerator.initvars
- @subclasses = Hash.new(nil)
- super
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.name
- return @name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.name=(name)
- @name = name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.namevar
- return @namevar || :name
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.namevar=(namevar)
- Blink.debug "Setting namevar for %s to %s" % [self,namevar]
- unless namevar.is_a? Symbol
- namevar = namevar.intern
- end
- @namevar = namevar
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def TypeGenerator.newtype(arghash)
- unless defined? @parameters
- raise "Type %s is set up incorrectly" % self
- end
-
- arghash.each { |key,value|
- if key.class != Symbol
- # convert to a symbol
- arghash[key.intern] = value
- arghash.delete key
- key = key.intern
- end
- unless @parameters.include?(key)
- raise "Invalid argument %s on class %s" %
- [key,self]
- end
-
- }
-
- # turn off automatically checking all arguments
- #@parameters.each { |option|
- # unless arghash.include?(option)
- # p arghash
- # raise "Must pass %s to class %s" %
- # [option,self]
- # end
- #}
-
- if @subclasses.include?(arghash[:name])
- raise "File type %s already exists" % arghash[:name]
- end
-
- klassname = arghash[:name].capitalize
-
- # create the file type
- Blink::Type.module_eval "
- class %s < %s
- end" % [klassname,self]
- klass = eval(klassname)
- klass.name = arghash[:name]
-
- @subclasses[arghash[:name]] = klass
-
- arghash.each { |option,value|
- method = option.id2name + "="
- if klass.respond_to?(method)
- #Blink.debug "Setting %s on %s to '%s'" % [option,klass,arghash[option]]
- klass.send(method,arghash[option])
- else
- Blink.debug "%s does not respond to %s" % [klass,method]
- end
- }
-
- # i couldn't get the method definition stuff to work
- # oh well
- # probably wouldn't want it in the end anyway
- #@parameters.each { |option|
- # writer = option.id2name + "="
- # readproc = proc { eval("@" + option.id2name) }
- # klass.send(:define_method,option,readproc)
- # writeproc = proc { |value| module_eval("@" + option.id2name) = value }
- # klass.send(:define_method,writer,writeproc)
- # klass.send(writer,hash[option])
- #}
-
- #Blink::Type.inherited(klass)
- Blink::Type.buildtypehash
- return klass
- end
- #---------------------------------------------------------------
-end
-#---------------------------------------------------------------
-end
-end
diff --git a/lib/blink/type/typegen/filerecord.rb b/lib/blink/type/typegen/filerecord.rb
deleted file mode 100644
index fb9030c12..000000000
--- a/lib/blink/type/typegen/filerecord.rb
+++ /dev/null
@@ -1,243 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# parse and write configuration files using objects with minimal parsing abilities
-
-require 'etc'
-require 'blink/type'
-require 'blink/type/typegen'
-
-#---------------------------------------------------------------
-class Blink::Type::FileRecord < Blink::Type::TypeGenerator
- attr_accessor :fields, :namevar, :splitchar, :object
-
- @parameters = [:name, :splitchar, :fields, :namevar, :filetype, :regex, :joinchar]
- @abstract = true
- @metaclass = true
-
- @namevar = :name
- @name = :filerecord
-
- #---------------------------------------------------------------
- def FileRecord.newtype(hash)
- #shortname = hash[:name]
- #hash[:name] = hash[:filetype].name.capitalize + hash[:name].capitalize
- klass = super(hash)
- #klass.name = shortname
- klass.parameters = hash[:fields]
- #klass.namevar = hash[:namevar]
- klass.filetype = hash[:filetype]
- hash.delete(:fields)
- hash.delete(:namevar)
- return klass
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.fields=(ary)
- @fields = ary
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.fields
- return @fields
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.filetype
- @filetype
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.filetype=(filetype)
- if filetype.is_a?(String)
- @filetype = Blink::Type::FileType[filetype]
- elsif filetype.is_a?(Blink::Type::FileType)
- @filetype = filetype
- else
- raise "Cannot use objects of type %s as filetypes" % filetype
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.joinchar=(char)
- @joinchar = char
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.joinchar
- unless defined? @joinchar
- @joinchar = nil
- end
- @joinchar
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.match(object,line)
- matchobj = nil
- begin
- matchobj = self.regex.match(line)
- rescue RegexpError => detail
- raise
- end
-
- if matchobj.nil?
- return nil
- else
- child = self.new(object)
- child.match = matchobj
- return child
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.regex=(regex)
- @regex = regex
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.regex
- # the only time @regex is allowed to be nil is if @splitchar is defined
- if @regex.nil?
- if @splitchar.nil?
- raise "%s defined incorrectly -- splitchar or regex must be specified" %
- self
- else
- ary = []
- text = @fields.collect { |field|
- "([^%s]*)" % @splitchar
- }.join(@splitchar)
- begin
- @regex = Regexp.new(text)
- rescue RegexpError => detail
- raise "Could not create splitregex from %s" % @splitchar
- end
- Blink.debug("Created regexp %s" % @regex)
- end
- elsif @regex.is_a?(String)
- begin
- @regex = Regexp.new(@regex)
- rescue RegexpError => detail
- raise "Could not create splitregex from %s" % @regex
- end
- end
- return @regex
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.splitchar=(char)
- @splitchar = char
- #@regex = %r{#{char}}
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileRecord.splitchar
- return @splitchar
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def [](field)
- # @parameters[field]
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def []=(field,value)
- # @parameters[field] = value
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def ==(other)
- unless self.class == other.class
- return false
- end
-
- unless self.name == other.name
- return false
- end
- @parameters.keys { |field|
- unless self[field] == other[field]
- Blink.debug("%s -> %s has changed" % [self.name, field])
- return false
- end
- }
- return true
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def initialize(hash)
- if self.class == Blink::Type::FileRecord
- self.class.newtype(hash)
- return
- end
- @parameters = {}
- #if block_given?
- # yield self
- #end
- super(hash)
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def match=(matchobj)
- @match = matchobj
- #puts "captures are [%s]" % [matchobj.captures]
- self.class.fields.zip(matchobj.captures) { |field,value|
- @parameters[field] = value
- #puts "%s => %s" % [field,@parameters[field]]
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def record=(record)
- begin
- ary = record.split(self.class.regex)
- rescue RegexpError=> detail
- raise RegexpError.new(detail)
- end
- self.class.fields.each { |field|
- @parameters[field] = ary.shift
- #puts "%s => %s" % [field,@parameters[field]]
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def name
- if @parameters.include?(self.class.namevar)
- return @parameters[self.class.namevar]
- else
- raise "No namevar '%s' for objects of type %s" %
- [self.class.namevar,self.class.to_s]
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def to_s
- ary = self.class.fields.collect { |field|
- if ! @parameters.include?(field)
- raise "Object %s is missing field %s" % [self.name,field]
- else
- @parameters[field]
- end
- }.join(self.class.joinchar || self.class.splitchar)
- end
- #---------------------------------------------------------------
-end
-#---------------------------------------------------------------
diff --git a/lib/blink/type/typegen/filetype.rb b/lib/blink/type/typegen/filetype.rb
deleted file mode 100644
index 4b3b89db8..000000000
--- a/lib/blink/type/typegen/filetype.rb
+++ /dev/null
@@ -1,316 +0,0 @@
-#!/usr/local/bin/ruby -w
-
-# $Id$
-
-# parse and write configuration files using objects with minimal parsing abilities
-
-require 'blink/type'
-require 'blink/type/typegen'
-
-class Blink::Type::FileType < Blink::Type::TypeGenerator
- attr_accessor :childtype
-
- @parameters = [:name, :linesplit, :escapednewlines]
- #@abstract = true
- @metaclass = true
-
- @namevar = :name
- @name = :filetype
-
- @modsystem = true
-
- #---------------------------------------------------------------
- def FileType.newtype(hash)
- unless hash.include?(:linesplit)
- hash[:linesplit] = "\n"
- end
-
- # i don't think there's any reason to 'super' this
- #klass = Blink::Type::TypeGenerator.newtype(hash)
- klass = super(hash)
-
- klass.escapednewlines = true
- klass.namevar = :name
- klass.parameters = [:name, :path, :complete]
-
- #klass.childtype = Blink::Type::FileRecord.newtype(
- # :name => hash[:name] + "_record",
- # :splitchar => hash[:recordsplit],
- # :fields => hash[:fields],
- # :namevar => hash[:namevar]
- #)
- #klass.addrecord(
- # :name => hash[:name] + "_record",
- # :splitchar => hash[:recordsplit],
- # :fields => hash[:fields],
- # :namevar => hash[:namevar]
- #)
-
- return klass
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # currently not used
- def FileType.addrecord(hash)
- unless defined? @records
- @records = {}
- end
- hash[:filetype] = self
-
- # default to the naming field being the first field provided
- unless hash.include?(:namevar)
- hash[:namevar] = hash[:fields][0]
- end
-
- recordtype = Blink::Type::FileRecord.newtype(hash)
- @records[recordtype.name] = recordtype
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.records
- return @records
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.escapednewlines=(value)
- @escnlines = value
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.escapednewlines
- if ! defined? @escnlines or @escnlines.nil?
- return false
- else
- return @escnlines
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.childtype
- unless defined? @childtype
- @childtype = nil
- end
- return @childtype
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.childtype=(childtype)
- @childtype = childtype
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.regex
- return @regex
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.linesplit=(linesplit)
- @regex = %r{#{linesplit}}
- @linesplit = linesplit
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def FileType.linesplit
- return @linesplit
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def [](name)
- # return @childhash[name]
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def []=(name,value)
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # we don't really have a 'less-than/greater-than' sense here
- # so i'm sticking with 'equals' until those make sense
- def ==(other)
- unless self.children.length == other.children.length
- Blink.debug("file has %s records instead of %s" %
- [self.children.length, other.children.length])
- return self.children.length == other.children.length
- end
- equal = true
- self.zip(other.children) { |schild,ochild|
- unless schild == ochild
- Blink.debug("%s has changed in %s" %
- [schild.name,self.name])
- equal = false
- break
- end
- }
-
- return equal
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # create a new record with a block
- def add(type,&block)
- obj = self.class.records[type].new(self,&block)
- Blink.debug("adding %s" % obj.name)
- @childary.push(obj)
- @childhash[obj.name] = obj
-
- return obj
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def children
- return @childary
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # remove a record
- def delete(name)
- if @childhash.has_key?(name)
- child = @childhash[name]
-
- @childhash.delete(child)
- @childary.delete(child)
- else
- raise "No such entry %s" % name
- end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def each
- @childary.each { |child|
- yield child
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # create a new file
- def initialize(hash)
- # if we are the FileType object itself, we create a new type
- # otherwise, we create an instance of an existing type
- # yes, this should be more straightforward
- if self.class == Blink::Type::FileType
- self.class.newtype(hash)
- return
- end
- Blink.debug "Creating new '%s' file with path '%s' and name '%s'" %
- [self.class.name,hash["path"],hash[:name]]
- Blink.debug hash.inspect
- @file = hash["path"]
-
- @childary = []
- @childhash = {}
- super
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # this is where we're pretty different from other objects
- # we can choose to either reparse the existing file and compare
- # the objects, or we can write our file out and do an
- # text comparison
- def insync?
- tmp = self.class.new(@file)
- tmp.retrieve
-
- return self == tmp
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- #def name
- # return @file
- #end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- # read the whole file in and turn it into each of the appropriate
- # objects
- def retrieve
- str = ""
- ::File.open(@file) { |fname|
- fname.each { |line|
- str += line
- }
- }
-
- if self.class.escapednewlines
- endreg = %r{\\\n\s*}
- str.gsub!(endreg,'')
- end
- @childary = str.split(self.class.regex).collect { |line|
- childobj = nil
- self.class.records.each { |name,recordtype|
- if childobj = recordtype.match(self,line)
- break
- end
- }
- if childobj.nil?
- Blink.warning("%s: could not match %s" % [self.name,line])
- #Blink.warning("could not match %s" % line)
- next
- end
-
- begin
- Blink.debug("got child: %s(%s)" % [childobj.class,childobj.to_s])
- rescue NoMethodError
- Blink.warning "Failed: %s" % childobj
- end
- childobj
- }.reject { |child|
- child.nil?
- }
-
- @childary.each { |child|
- begin
- @childhash[child.name] = child
- rescue NoMethodError => detail
- p child
- p child.class
- puts detail
- exit
- end
- }
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def sync
- #unless self.insync?
- self.write
- #end
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def to_s
- return @childary.collect { |child|
- child.to_s
- }.join(self.class.linesplit) + self.class.linesplit
- end
- #---------------------------------------------------------------
-
- #---------------------------------------------------------------
- def write
- ::File.open(@file, "w") { |file|
- file.write(self.to_s)
- }
- end
- #---------------------------------------------------------------
-end
-#---------------------------------------------------------------