copland 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/doc/manual-html/chapter-1.html +227 -36
  2. data/doc/manual-html/chapter-10.html +155 -82
  3. data/doc/manual-html/chapter-11.html +90 -267
  4. data/doc/manual-html/chapter-12.html +289 -71
  5. data/doc/manual-html/chapter-13.html +430 -0
  6. data/doc/manual-html/chapter-2.html +45 -21
  7. data/doc/manual-html/chapter-3.html +45 -21
  8. data/doc/manual-html/chapter-4.html +45 -21
  9. data/doc/manual-html/chapter-5.html +45 -21
  10. data/doc/manual-html/chapter-6.html +49 -21
  11. data/doc/manual-html/chapter-7.html +45 -21
  12. data/doc/manual-html/chapter-8.html +66 -26
  13. data/doc/manual-html/chapter-9.html +48 -24
  14. data/doc/manual-html/index.html +54 -22
  15. data/doc/manual-html/manual.css +12 -0
  16. data/doc/manual-html/tutorial-1.html +45 -21
  17. data/doc/manual-html/tutorial-2.html +45 -21
  18. data/doc/manual-html/tutorial-3.html +45 -21
  19. data/doc/manual-html/tutorial-4.html +45 -21
  20. data/doc/manual-html/tutorial-5.html +45 -21
  21. data/doc/manual/manual.css +12 -0
  22. data/doc/manual/manual.rb +1 -1
  23. data/doc/manual/manual.yml +426 -20
  24. data/doc/packages/copland.html +41 -9
  25. data/doc/packages/copland.lib.html +36 -8
  26. data/doc/packages/copland.remote.html +46 -10
  27. data/doc/packages/copland.webrick.html +16 -65
  28. data/doc/packages/index.html +1 -1
  29. data/doc/presentation/copland.mgp +1083 -0
  30. data/doc/presentation/to_html.rb +52 -0
  31. data/lib/copland/configuration-point/common.rb +32 -1
  32. data/lib/copland/configuration/yaml/service-point.rb +10 -1
  33. data/lib/copland/log-factory.rb +28 -12
  34. data/lib/copland/logger.rb +155 -0
  35. data/lib/copland/models/singleton.rb +8 -2
  36. data/lib/copland/package.rb +32 -14
  37. data/lib/copland/service-point.rb +7 -0
  38. data/lib/copland/thread.rb +104 -0
  39. data/lib/copland/utils.rb +10 -3
  40. data/lib/copland/version.rb +2 -2
  41. data/test/configuration/yaml/tc_service-point-processor.rb +8 -0
  42. data/test/custom-logger.yml +2 -1
  43. data/test/impl/tc_logging-interceptor.rb +12 -12
  44. data/test/logger.yml +1 -1
  45. data/test/mock.rb +2 -0
  46. data/test/tc_logger.rb +19 -6
  47. data/test/tc_package.rb +25 -0
  48. data/test/tc_queryable-mutex.rb +75 -0
  49. data/test/tc_registry.rb +8 -4
  50. metadata +9 -2
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'redcloth'
5
+
6
+ $target_dir = "html"
7
+ $resolution = "800x600"
8
+ $presentation = "copland2.mgp"
9
+ $image_format = "png"
10
+
11
+ FileUtils.rm_rf $target_dir
12
+ FileUtils.mkdir_p $target_dir
13
+ system "mgp -g #{$resolution} -D #{$target_dir} -E #{$image_format} #{$presentation}"
14
+
15
+ puts "======================================================="
16
+ puts "inserting slide notes into generated HTML..."
17
+
18
+ content = File.read( $presentation )
19
+ pages = content.split( /%page/ ).map { |p| p.strip }
20
+ head = pages.shift
21
+
22
+ pages.each_with_index do |page, index|
23
+ if page =~ /^%%==*\n(.*)\n%%==*\n/m
24
+ note = $1.gsub( /^%% ?/, "" ).strip
25
+
26
+ if note.length > 0
27
+ slide_file = File.join( $target_dir, "mgp%05d.html" % (index+1) )
28
+ slide_html = File.read( slide_file )
29
+
30
+ note.gsub!( /(\S)\n([ \t]*\w)/, '\1 \2' )
31
+
32
+ if slide_html =~ /<IMG SRC=\"mgp.*png\".*><BR>/
33
+ before = $` + $&
34
+ after = $'
35
+ new_html = before + "\n" +
36
+ "<style type=\"text/css\">" +
37
+ "#notes h1 { font-size: large }\n" +
38
+ "#notes h2 { font-size: normal }\n" +
39
+ "#notes h3 { font-size: normal }\n" +
40
+ "</style>" +
41
+ "<h2>Slide Notes:</h2>" +
42
+ "<blockquote id=\"notes\">" +
43
+ RedCloth.new(note).to_html +
44
+ "</blockquote>" +
45
+ after
46
+ puts " writing slide #{index+1}"
47
+ File.open( slide_file, "w" ) { |file| file.write new_html }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ puts "done!"
@@ -156,10 +156,11 @@ module Copland
156
156
  # for this configuration point; or if no schema exists, run the
157
157
  # values through the Copland::translate_values method.
158
158
  def process_values( values )
159
+ Thread.current[:disable_symbol_substitution] = true
159
160
  s = schema
160
161
  contributor = contributor_of( values )
161
162
  if s.respond_to?( :process )
162
- if s.is_a?( Array )
163
+ if values.is_a?( Array )
163
164
  values = values.map! { |value|
164
165
  s.process( self, contributor, value )
165
166
  }
@@ -172,7 +173,37 @@ module Copland
172
173
  end
173
174
 
174
175
  return values
176
+ ensure
177
+ Thread.current[:disable_symbol_substitution] = false
178
+ end
179
+
180
+ # Processes all substitution symbols in the values of this configuration
181
+ # point, in place. This has a guard to ensure that it is only executed
182
+ # once.
183
+ def substitute_symbols!
184
+ Thread.critical = true
185
+ return if @substitution_processed
186
+ @substitution_processed = true
187
+ substitute_symbols( self )
188
+ ensure
189
+ Thread.critical = false
190
+ end
191
+
192
+ # The recursive portion of the symbol substitution step. This looks for
193
+ # substitution symbols in all of the values of the configuration point,
194
+ # substituting them as they are found.
195
+ def substitute_symbols( value )
196
+ case value
197
+ when Hash
198
+ value.each_pair { |k,v| value[k] = substitute_symbols( v ) }
199
+ when Array
200
+ value.map! { |v| substitute_symbols( v ) }
201
+ when String
202
+ Copland::substitute_symbols( owner.registry, value )
203
+ end
204
+ value
175
205
  end
206
+ private :substitute_symbols
176
207
 
177
208
  # Returns false. Once a configuration point is fixated, it will include
178
209
  # the Copland::ConfigurationPoint::ConfigurationPointFunctionality::Fixated
@@ -62,7 +62,8 @@ module Copland
62
62
 
63
63
  # The list of valid keys that may exist on the input.
64
64
  VALID_KEYS = [ "description", "model", "implementor",
65
- "interceptors", "listen-to", "schema" ]
65
+ "interceptors", "listen-to", "schema",
66
+ "visibility" ]
66
67
 
67
68
  # The list of required keys that must exist on the input.
68
69
  REQUIRED_KEYS = [ "implementor" ]
@@ -101,6 +102,14 @@ module Copland
101
102
  when "schema"
102
103
  point.schema = @schema.process( point, value )
103
104
 
105
+ when "visibility"
106
+ point.visibility = case value.downcase
107
+ when "public" then :public
108
+ when "private" then :private
109
+ else raise ParserError,
110
+ "invalid visibility value #{value.inspect}"
111
+ end
112
+
104
113
  else
105
114
  raise CoplandBug, "[BUG] invalid key discovered too late: #{key.inspect}"
106
115
  end
@@ -31,7 +31,7 @@
31
31
  # =============================================================================
32
32
  #++
33
33
 
34
- require 'logger'
34
+ require 'copland/logger'
35
35
  require 'thread'
36
36
  require 'yaml'
37
37
 
@@ -51,6 +51,9 @@ module Copland
51
51
  # The default name of the log file to write to.
52
52
  DEFAULT_LOG_FILENAME = "./copland.log"
53
53
 
54
+ # The default format of the log messages (see Logger for more info)
55
+ DEFAULT_MESSAGE_FORMAT = "[%-5p] %d -- %C: %m"
56
+
54
57
  # Translate names of levels to their actual values.
55
58
  LEVEL_TRANSLATOR = {
56
59
  "DEBUG" => Logger::DEBUG,
@@ -61,8 +64,11 @@ module Copland
61
64
  "UNKNOWN" => Logger::UNKNOWN
62
65
  }
63
66
 
64
- # The default (date) format string to use when logging.
65
- attr_reader :default_format
67
+ # The default date format string to use when logging.
68
+ attr_reader :default_date_format
69
+
70
+ # The default message format string to use when logging.
71
+ attr_reader :default_message_format
66
72
 
67
73
  # The default log level to use for logs that are created.
68
74
  attr_reader :default_level
@@ -82,11 +88,14 @@ module Copland
82
88
  # rolled.
83
89
  # * <tt>:roll_frequency</tt>: either 'daily', 'weekly', or 'monthly'.
84
90
  # * <tt>:roll_size</tt>: the maximum size of a log file.
85
- # * <tt>:default_format</tt>: the default date format string for the log.
91
+ # * <tt>:default_date_format</tt>: the default date format string for the
92
+ # log.
93
+ # * <tt>:default_message_format</tt>: the default message format string for
94
+ # the log.
86
95
  # * <tt>:default_level</tt>: the default log level for the log.
87
96
  # * <tt>:levels</tt>: a hash of patterns that map to a hash of 'level'
88
- # and 'format' keys, specifying the log level and date format for any
89
- # log whose name matches the key.
97
+ # 'date_format', and 'message_format' keys, specifying the log level,
98
+ # date format, and message format for any log whose name matches the key.
90
99
  def initialize( opts={} )
91
100
  opts[ :config_file ] ||= DEFAULT_CONFIG_FILE
92
101
  read_config opts
@@ -102,10 +111,13 @@ module Copland
102
111
  :shift_size => roll_size )
103
112
  end
104
113
 
105
- @default_format = opts[ :default_format ]
114
+ @default_date_format = opts[ :default_date_format ]
115
+ @default_message_format = opts[ :default_message_format ] ||
116
+ DEFAULT_MESSAGE_FORMAT
106
117
  @default_level = opts[ :default_level ]
107
118
 
108
- @levels = Hash.new "level" => nil, "format" => nil
119
+ @levels = Hash.new "level" => nil, "date-format" => nil,
120
+ "message-format" => nil
109
121
 
110
122
  ( opts[ :levels ] || Hash.new ).each_pair do |key, value|
111
123
  key = Regexp.new( "^" + key.gsub( /\./, "\\." ).gsub( /\*/, ".*" ) )
@@ -125,7 +137,8 @@ module Copland
125
137
  file = opts[ :config_file ]
126
138
  if File.exist?( file ) && File.readable?( file )
127
139
  config = YAML.load( File.read( file ) )
128
- opts[ :default_format ] ||= config[ "default-format" ]
140
+ opts[ :default_date_format ] ||= config[ "default-date-format" ]
141
+ opts[ :default_message_format ] ||= config[ "default-message-format" ]
129
142
  opts[ :default_level ] ||=
130
143
  LEVEL_TRANSLATOR[ config[ "default-level" ] ] ||
131
144
  config[ "default-level" ]
@@ -169,14 +182,17 @@ module Copland
169
182
  definition = find_definition( name )
170
183
 
171
184
  level = definition[ "level" ] || @default_level
172
- format = definition[ "format" ] || @default_format
185
+ date_format = definition[ "date-format" ] || @default_date_format
186
+ message_format = definition[ "message-format" ] ||
187
+ @default_message_format
173
188
 
174
189
  level = LEVEL_TRANSLATOR[ level ] || level
175
190
 
176
- logger = Logger.new( @device )
191
+ logger = Copland::Logger.new( @device )
177
192
  logger.level = level if level
178
- logger.datetime_format = format if format
179
193
  logger.progname = name
194
+ logger.datetime_format = date_format if date_format
195
+ logger.message_format = message_format if message_format
180
196
 
181
197
  @loggers[ name ] = logger
182
198
  return logger
@@ -0,0 +1,155 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ #
9
+ # * Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ #
12
+ # * Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # * The names of its contributors may not be used to endorse or promote
17
+ # products derived from this software without specific prior written
18
+ # permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
+ # POSSIBILITY OF SUCH DAMAGE.
31
+ # =============================================================================
32
+ #++
33
+
34
+ require 'logger'
35
+ require 'strscan'
36
+ require 'copland/errors'
37
+
38
+ module Copland
39
+
40
+ # A specialization of the standard Logger class that comes with Ruby. This
41
+ # provides the additional functionality of a fully-customizable message
42
+ # format, whereas the original only provided a customizable date format.
43
+ class Logger < ::Logger
44
+
45
+ # The brief name of this logger (derived from #progname).
46
+ attr_reader :name
47
+
48
+ # The format string for the message (+nil+ if the default should be used)
49
+ attr_reader :message_format
50
+
51
+ # The map of specifier options supported by this class.
52
+ SPECIFIER_OPTIONS = {
53
+ "c" => { :type => "s", :value => "@name" },
54
+ "C" => { :type => "s", :value => "self.progname" },
55
+ "d" => { :type => "s", :value => "opts[:timestamp]" },
56
+ "F" => { :type => "s", :value => "opts[:caller_file]" },
57
+ "l" => { :type => "s", :value => "opts[:caller_info]" },
58
+ "L" => { :type => "d", :value => "opts[:caller_line]" },
59
+ "m" => { :type => "s", :value => "opts[:msg]" },
60
+ "M" => { :type => "s", :value => "opts[:caller_method]" },
61
+ "n" => { :type => "s", :value => "$/" },
62
+ "p" => { :type => "s", :value => "opts[:severity]" },
63
+ "t" => { :type => "d", :value => "Thread.current.__id__" },
64
+ "%" => { :type => "s", :value => "'%'" },
65
+ "P" => { :type => "s", :value => "opts[:progname]" },
66
+ "$" => { :type => "d", :value => "$$" }
67
+ }
68
+
69
+ # The regular expression for matching specifier patterns in the
70
+ # format strings.
71
+ SPECIFIER_PATTERN = /(.*?)%(-?\d*(?:\.\d+)?)?([cCdFlLmMnpt%$P])/
72
+
73
+ # Extracts the unqualified name from the progname, after setting the
74
+ # progname.
75
+ def progname=( progname )
76
+ super
77
+ @name = progname.split( /\./ ).last
78
+ end
79
+
80
+ # Set the message format string to the given string. This also pre-parses
81
+ # the format for faster processing.
82
+ #
83
+ # The format string is a printf-formatted string, which supports the following
84
+ # format specifiers:
85
+ #
86
+ # c:: the unqualified name of the logger
87
+ # C:: the fully-qualified name of the logger
88
+ # d:: the date/time string (as formatted by the #datetime_format string)
89
+ # F:: the filename of the calling routine
90
+ # l:: the location of the calling routine
91
+ # L:: the line number of the calling routine
92
+ # m:: the message to log
93
+ # M:: the name of the calling method
94
+ # n:: the newline character
95
+ # p:: the name of the priority (or severity) used to log this method
96
+ # t:: the id of the current thread
97
+ # %:: a percentage character
98
+ # P:: the progname that was passed to the logger method
99
+ # $:: the current process id
100
+ def message_format=( format )
101
+ @message_format = format
102
+ return unless format
103
+
104
+ @needs_caller_info = false
105
+
106
+ format_string = ""
107
+ format_parameters = []
108
+
109
+ @message_format_tokens = []
110
+ format.scan( SPECIFIER_PATTERN ) do |v|
111
+ format_string << v[0] if v[0].length > 0
112
+ opts = SPECIFIER_OPTIONS[ v[2] ]
113
+ format_string << "%#{v[1]}#{opts[:type]}"
114
+ format_parameters << opts[:value]
115
+ @needs_caller_info = true if v[2] =~ /[FlLM]/
116
+ end
117
+ format_string << $' if $'.length > 0
118
+ format_string << "\n"
119
+
120
+ definition = "proc { |opts| #{format_string.inspect} % [ #{format_parameters.join(',')} ] }"
121
+ @message_formatter = eval( definition )
122
+ end
123
+
124
+ # Format the message using the given parameters. If a message format has
125
+ # not been given, just call the superclass's implementation. Otherwise,
126
+ # process the tokens from the parsed message format.
127
+ def format_message( severity, timestamp, msg, progname )
128
+ if @message_format.nil?
129
+ super
130
+ else
131
+ opts = {
132
+ :severity => severity,
133
+ :timestamp => timestamp,
134
+ :msg => msg,
135
+ :progname => progname
136
+ }
137
+
138
+ if @needs_caller_info
139
+ stack = caller
140
+ stack.shift while stack.first =~ /\blogger\.rb/
141
+ opts[:caller_info] = caller_info = stack.first
142
+ match = caller_info.match( /(.*):(\d+)(?::in `(.*)')?/ )
143
+ opts[:caller_file] = match[1]
144
+ opts[:caller_line] = match[2]
145
+ opts[:caller_method] = match[3]
146
+ end
147
+
148
+ @message_formatter.call( opts )
149
+ end
150
+ end
151
+ private :format_message
152
+
153
+ end
154
+
155
+ end
@@ -31,8 +31,9 @@
31
31
  # =============================================================================
32
32
  #++
33
33
 
34
+ require 'copland/errors'
34
35
  require 'copland/models/abstract'
35
- require 'thread'
36
+ require 'copland/thread'
36
37
 
37
38
  module Copland
38
39
 
@@ -47,7 +48,7 @@ module Copland
47
48
  # Create a new SingletonServiceModel on the given service point.
48
49
  def initialize( service_point )
49
50
  super
50
- @mutex = Mutex.new
51
+ @mutex = QueryableMutex.new
51
52
  end
52
53
 
53
54
  # Return the singleton instance of the service point. This is
@@ -55,6 +56,11 @@ module Copland
55
56
  def instance( &init )
56
57
  return @instance if @instance
57
58
 
59
+ if @mutex.self_locked?
60
+ raise CoplandException,
61
+ "circular dependency on #{service_point.full_name} detected"
62
+ end
63
+
58
64
  @mutex.synchronize do
59
65
  return @instance if @instance
60
66
  new_instance( &init )
@@ -78,14 +78,24 @@ module Copland
78
78
  end
79
79
 
80
80
  # Returns the service point with the given name. If no such service point
81
- # exists, this returns +nil+.
82
- def service_point( name )
83
- @service_points[ name ]
81
+ # exists, this returns +nil+. If +include_private+ is false, then this will
82
+ # also return +nil+ if the point exists, but is marked private.
83
+ def service_point( name, include_private=false )
84
+ point = @service_points[ name ]
85
+ return nil if point.nil? || point.visibility == :private &&
86
+ !include_private
87
+ point
84
88
  end
85
89
 
86
- # This returns the names of all service points in the package.
87
- def service_points
88
- @service_points.keys.freeze
90
+ # This returns the names of all service points in the package. If
91
+ # +include_private+ is true (the default), then only the public service
92
+ # points will be returned.
93
+ def service_points( include_private=false )
94
+ names = @service_points.keys.dup
95
+ if !include_private
96
+ names.reject! { |p| @service_points[p].visibility != :public }
97
+ end
98
+ names
89
99
  end
90
100
 
91
101
  # This instantiates the service point with the given name and returns the
@@ -95,15 +105,19 @@ module Copland
95
105
  # If a block is given, it will be used to initialize the service (but only
96
106
  # when the service is created--if the service is a singleton service and
97
107
  # it was created previously, the init block will be ignored).
98
- def service( name, &init )
99
- point = service_point( name ) or raise ServicePointNotFound, name
108
+ #
109
+ # If +include_private+ is true, then only public service points may be
110
+ # instantiated.
111
+ def service( name, include_private=false, &init )
112
+ point = service_point( name, include_private ) or
113
+ raise ServicePointNotFound, name
100
114
  point.instance( &init )
101
115
  end
102
116
 
103
117
  # Returns +true+ if the named service exists in this package, and +false+
104
118
  # otherwise.
105
- def service_exist?( name )
106
- return !service_point( name ).nil?
119
+ def service_exist?( name, include_private=false )
120
+ return !service_point( name, include_private ).nil?
107
121
  end
108
122
 
109
123
  # This is a convenience method for returning a service with the given name,
@@ -155,8 +169,10 @@ module Copland
155
169
  end
156
170
 
157
171
  # Iterates over each service point in the package.
158
- def each_service_point( &block )
159
- @service_points.each_value( &block )
172
+ def each_service_point( include_private=false, &block )
173
+ @service_points.each_value do |pt|
174
+ yield pt if pt.visibility == :public || include_private
175
+ end
160
176
  self
161
177
  end
162
178
 
@@ -171,8 +187,10 @@ module Copland
171
187
  end
172
188
 
173
189
  # Returns the number of service points in the package.
174
- def service_point_count
175
- @service_points.size
190
+ def service_point_count( include_private=false )
191
+ @service_points.
192
+ reject { |k,v| v.visibility == :private && !include_private }.
193
+ size
176
194
  end
177
195
 
178
196
  # Returns the number of configuration points in the package.