puppet 3.6.0 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (39) hide show
  1. data/ext/rack/example-passenger-vhost.conf +3 -2
  2. data/lib/puppet.rb +10 -0
  3. data/lib/puppet/agent.rb +0 -1
  4. data/lib/puppet/application.rb +16 -18
  5. data/lib/puppet/configurer.rb +16 -21
  6. data/lib/puppet/context.rb +46 -5
  7. data/lib/puppet/indirector/catalog/compiler.rb +4 -3
  8. data/lib/puppet/indirector/rest.rb +5 -6
  9. data/lib/puppet/network/http/route.rb +15 -6
  10. data/lib/puppet/parser/functions/epp.rb +1 -1
  11. data/lib/puppet/pops/evaluator/access_operator.rb +40 -35
  12. data/lib/puppet/pops/parser/egrammar.ra +5 -1
  13. data/lib/puppet/pops/parser/eparser.rb +1096 -1079
  14. data/lib/puppet/pops/parser/interpolation_support.rb +1 -1
  15. data/lib/puppet/pops/parser/lexer2.rb +14 -7
  16. data/lib/puppet/provider/package/rpm.rb +3 -0
  17. data/lib/puppet/provider/package/yum.rb +15 -7
  18. data/lib/puppet/provider/package/zypper.rb +3 -2
  19. data/lib/puppet/ssl/host.rb +1 -1
  20. data/lib/puppet/test/test_helper.rb +34 -1
  21. data/lib/puppet/type/package.rb +12 -1
  22. data/lib/puppet/version.rb +1 -1
  23. data/spec/unit/configurer_spec.rb +36 -7
  24. data/spec/unit/context_spec.rb +39 -0
  25. data/spec/unit/indirector/catalog/compiler_spec.rb +16 -0
  26. data/spec/unit/indirector/rest_spec.rb +17 -3
  27. data/spec/unit/network/http/route_spec.rb +16 -0
  28. data/spec/unit/parser/functions/epp_spec.rb +15 -0
  29. data/spec/unit/pops/evaluator/access_ops_spec.rb +6 -0
  30. data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +10 -0
  31. data/spec/unit/pops/parser/lexer2_spec.rb +10 -2
  32. data/spec/unit/provider/package/aptrpm_spec.rb +0 -1
  33. data/spec/unit/provider/package/rpm_spec.rb +8 -2
  34. data/spec/unit/provider/package/yum_spec.rb +40 -23
  35. data/spec/unit/provider/package/zypper_spec.rb +25 -6
  36. data/spec/unit/ssl/host_spec.rb +5 -5
  37. data/spec/unit/type/package_spec.rb +21 -1
  38. metadata +3157 -3147
  39. checksums.yaml +0 -7
@@ -18,8 +18,9 @@ Listen 8140
18
18
 
19
19
  <VirtualHost *:8140>
20
20
  SSLEngine on
21
- SSLProtocol -ALL +SSLv3 +TLSv1
22
- SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:-LOW:-SSLv2:-EXP
21
+ SSLProtocol ALL -SSLv2
22
+ SSLCipherSuite ALL:!aNULL:!eNULL:!DES:!3DES:!IDEA:!SEED:!DSS:!PSK:!RC4:!MD5:+HIGH:+MEDIUM:!LOW:!SSLv2:!EXP
23
+ SSLHonorCipherOrder on
23
24
 
24
25
  SSLCertificateFile /etc/puppet/ssl/certs/squigley.namespace.at.pem
25
26
  SSLCertificateKeyFile /etc/puppet/ssl/private_keys/squigley.namespace.at.pem
data/lib/puppet.rb CHANGED
@@ -234,6 +234,16 @@ module Puppet
234
234
  @context.override(bindings, description, &block)
235
235
  end
236
236
 
237
+ # @api private
238
+ def self.mark_context(name)
239
+ @context.mark(name)
240
+ end
241
+
242
+ # @api private
243
+ def self.rollback_context(name)
244
+ @context.rollback(name)
245
+ end
246
+
237
247
  require 'puppet/node'
238
248
 
239
249
  # The single instance used for normal operation
data/lib/puppet/agent.rb CHANGED
@@ -11,7 +11,6 @@ class Puppet::Agent
11
11
 
12
12
  attr_reader :client_class, :client, :splayed, :should_fork
13
13
 
14
- # Just so we can specify that we are "the" instance.
15
14
  def initialize(client_class, should_fork=true)
16
15
  @splayed = false
17
16
 
@@ -353,24 +353,22 @@ class Application
353
353
  plugin_hook('initialize_app_defaults') { initialize_app_defaults }
354
354
  end
355
355
 
356
- Puppet.override(Puppet.base_context(Puppet.settings)) do
357
- configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])
358
- configured_environment = configured_environment.override_from_commandline(Puppet.settings)
359
-
360
- # Setup a new context using the app's configuration
361
- Puppet.override({ :current_environment => configured_environment },
362
- "New base context and current environment from application's configuration") do
363
- require 'puppet'
364
- require 'puppet/util/instrumentation'
365
- Puppet::Util::Instrumentation.init
366
-
367
- exit_on_fail("initialize") { plugin_hook('preinit') { preinit } }
368
- exit_on_fail("parse application options") { plugin_hook('parse_options') { parse_options } }
369
- exit_on_fail("prepare for execution") { plugin_hook('setup') { setup } }
370
- exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes }
371
- exit_on_fail("run") { plugin_hook('run_command') { run_command } }
372
- end
373
- end
356
+ Puppet.push_context(Puppet.base_context(Puppet.settings), "Update for application settings (#{self.class.run_mode})")
357
+ configured_environment = Puppet.lookup(:environments).get(Puppet[:environment])
358
+ configured_environment = configured_environment.override_from_commandline(Puppet.settings)
359
+
360
+ # Setup a new context using the app's configuration
361
+ Puppet.push_context({ :current_environment => configured_environment },
362
+ "Update current environment from application's configuration")
363
+
364
+ require 'puppet/util/instrumentation'
365
+ Puppet::Util::Instrumentation.init
366
+
367
+ exit_on_fail("initialize") { plugin_hook('preinit') { preinit } }
368
+ exit_on_fail("parse application options") { plugin_hook('parse_options') { parse_options } }
369
+ exit_on_fail("prepare for execution") { plugin_hook('setup') { setup } }
370
+ exit_on_fail("configure routes from #{Puppet[:route_file]}") { configure_indirector_routes }
371
+ exit_on_fail("run") { plugin_hook('run_command') { run_command } }
374
372
  end
375
373
 
376
374
  def main
@@ -22,13 +22,6 @@ class Puppet::Configurer
22
22
  "Puppet configuration client"
23
23
  end
24
24
 
25
- class << self
26
- # Puppet agent should only have one instance running, and we need a
27
- # way to retrieve it.
28
- attr_accessor :instance
29
- include Puppet::Util
30
- end
31
-
32
25
  def execute_postrun_command
33
26
  execute_from_setting(:postrun_command)
34
27
  end
@@ -51,11 +44,9 @@ class Puppet::Configurer
51
44
  end
52
45
  end
53
46
 
54
- # Just so we can specify that we are "the" instance.
55
47
  def initialize
56
48
  Puppet.settings.use(:main, :ssl, :agent)
57
49
 
58
- self.class.instance = self
59
50
  @running = false
60
51
  @splayed = false
61
52
  @environment = Puppet[:environment]
@@ -121,8 +112,6 @@ class Puppet::Configurer
121
112
  def apply_catalog(catalog, options)
122
113
  report = options[:report]
123
114
  report.configuration_version = catalog.version
124
- report.transaction_uuid = @transaction_uuid
125
- report.environment = @environment
126
115
 
127
116
  benchmark(:notice, "Finished catalog run") do
128
117
  catalog.apply(options)
@@ -132,15 +121,15 @@ class Puppet::Configurer
132
121
  report
133
122
  end
134
123
 
135
- def get_transaction_uuid
136
- { :transaction_uuid => @transaction_uuid }
137
- end
138
-
139
124
  # The code that actually runs the catalog.
140
125
  # This just passes any options on to the catalog,
141
126
  # which accepts :tags and :ignoreschedules.
142
127
  def run(options = {})
143
- options[:report] ||= Puppet::Transaction::Report.new("apply")
128
+ # We create the report pre-populated with default settings for
129
+ # environment and transaction_uuid very early, this is to ensure
130
+ # they are sent regardless of any catalog compilation failures or
131
+ # exceptions.
132
+ options[:report] ||= Puppet::Transaction::Report.new("apply", nil, @environment, @transaction_uuid)
144
133
  report = options[:report]
145
134
  init_storage
146
135
 
@@ -155,10 +144,12 @@ class Puppet::Configurer
155
144
  unless options[:catalog]
156
145
  begin
157
146
  if node = Puppet::Node.indirection.find(Puppet[:node_name_value],
158
- :environment => @environment, :ignore_cache => true)
147
+ :environment => @environment, :ignore_cache => true, :transaction_uuid => @transaction_uuid,
148
+ :fail_on_404 => true)
159
149
  if node.environment.to_s != @environment
160
150
  Puppet.warning "Local environment: \"#{@environment}\" doesn't match server specified node environment \"#{node.environment}\", switching agent to \"#{node.environment}\"."
161
151
  @environment = node.environment.to_s
152
+ report.environment = @environment
162
153
  query_options = nil
163
154
  end
164
155
  end
@@ -172,8 +163,9 @@ class Puppet::Configurer
172
163
 
173
164
  query_options = get_facts(options) unless query_options
174
165
 
175
- # add the transaction uuid to the catalog query options hash
176
- query_options.merge! get_transaction_uuid if query_options
166
+ # get_facts returns nil during puppet apply
167
+ query_options ||= {}
168
+ query_options[:transaction_uuid] = @transaction_uuid
177
169
 
178
170
  unless catalog = prepare_and_retrieve_catalog(options, query_options)
179
171
  return nil
@@ -190,6 +182,7 @@ class Puppet::Configurer
190
182
  end
191
183
  Puppet.warning "Local environment: \"#{@environment}\" doesn't match server specified environment \"#{catalog.environment}\", restarting agent run with environment \"#{catalog.environment}\""
192
184
  @environment = catalog.environment
185
+ report.environment = @environment
193
186
  return nil unless catalog = prepare_and_retrieve_catalog(options, query_options)
194
187
  tries += 1
195
188
  end
@@ -247,7 +240,8 @@ class Puppet::Configurer
247
240
  def retrieve_catalog_from_cache(query_options)
248
241
  result = nil
249
242
  @duration = thinmark do
250
- result = Puppet::Resource::Catalog.indirection.find(Puppet[:node_name_value], query_options.merge(:ignore_terminus => true, :environment => @environment))
243
+ result = Puppet::Resource::Catalog.indirection.find(Puppet[:node_name_value],
244
+ query_options.merge(:ignore_terminus => true, :environment => @environment))
251
245
  end
252
246
  Puppet.notice "Using cached catalog"
253
247
  result
@@ -259,7 +253,8 @@ class Puppet::Configurer
259
253
  def retrieve_new_catalog(query_options)
260
254
  result = nil
261
255
  @duration = thinmark do
262
- result = Puppet::Resource::Catalog.indirection.find(Puppet[:node_name_value], query_options.merge(:ignore_cache => true, :environment => @environment))
256
+ result = Puppet::Resource::Catalog.indirection.find(Puppet[:node_name_value],
257
+ query_options.merge(:ignore_cache => true, :environment => @environment, :fail_on_404 => true))
263
258
  end
264
259
  result
265
260
  rescue SystemExit,NoMemoryError
@@ -14,26 +14,32 @@ class Puppet::Context
14
14
  class UndefinedBindingError < Puppet::Error; end
15
15
  class StackUnderflow < Puppet::Error; end
16
16
 
17
+ class UnknownRollbackMarkError < Puppet::Error; end
18
+ class DuplicateRollbackMarkError < Puppet::Error; end
19
+
17
20
  # @api private
18
21
  def initialize(initial_bindings)
19
- @stack = []
20
22
  @table = initial_bindings
21
23
  @description = "root"
24
+ @id = 0
25
+ @rollbacks = {}
26
+ @stack = [[0, nil, nil]]
22
27
  end
23
28
 
24
29
  # @api private
25
30
  def push(overrides, description = "")
26
- @stack.push([@table, @description])
31
+ @id += 1
32
+ @stack.push([@id, @table, @description])
27
33
  @table = @table.merge(overrides || {})
28
34
  @description = description
29
35
  end
30
36
 
31
37
  # @api private
32
38
  def pop
33
- if @stack.empty?
39
+ if @stack[-1][0] == 0
34
40
  raise(StackUnderflow, "Attempted to pop, but already at root of the context stack.")
35
41
  else
36
- (@table, @description) = @stack.pop
42
+ (_, @table, @description) = @stack.pop
37
43
  end
38
44
  end
39
45
 
@@ -51,10 +57,45 @@ class Puppet::Context
51
57
 
52
58
  # @api private
53
59
  def override(bindings, description = "", &block)
60
+ mark_point = "override over #{@stack[-1][0]}"
61
+ mark(mark_point)
54
62
  push(bindings, description)
55
63
 
56
64
  yield
57
65
  ensure
58
- pop
66
+ rollback(mark_point)
67
+ end
68
+
69
+ # Mark a place on the context stack to later return to with {rollback}.
70
+ #
71
+ # @param name [Object] The identifier for the mark
72
+ #
73
+ # @api private
74
+ def mark(name)
75
+ if @rollbacks[name].nil?
76
+ @rollbacks[name] = @stack[-1][0]
77
+ else
78
+ raise DuplicateRollbackMarkError, "Mark for '#{name}' already exists"
79
+ end
80
+ end
81
+
82
+ # Roll back to a mark set by {mark}.
83
+ #
84
+ # Rollbacks can only reach a mark accessible via {pop}. If the mark is not on
85
+ # the current context stack the behavior of rollback is undefined.
86
+ #
87
+ # @param name [Object] The identifier for the mark
88
+ #
89
+ # @api private
90
+ def rollback(name)
91
+ if @rollbacks[name].nil?
92
+ raise UnknownRollbackMarkError, "Unknown mark '#{name}'"
93
+ end
94
+
95
+ while @stack[-1][0] != @rollbacks[name]
96
+ pop
97
+ end
98
+
99
+ @rollbacks.delete(name)
59
100
  end
60
101
  end
@@ -104,11 +104,12 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
104
104
  end
105
105
 
106
106
  # Turn our host name into a node object.
107
- def find_node(name, environment)
107
+ def find_node(name, environment, transaction_uuid)
108
108
  Puppet::Util::Profiler.profile("Found node information") do
109
109
  node = nil
110
110
  begin
111
- node = Puppet::Node.indirection.find(name, :environment => environment)
111
+ node = Puppet::Node.indirection.find(name, :environment => environment,
112
+ :transaction_uuid => transaction_uuid)
112
113
  rescue => detail
113
114
  message = "Failed when searching for node #{name}: #{detail}"
114
115
  Puppet.log_exception(detail, message)
@@ -143,7 +144,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
143
144
  # node's catalog with only one certificate and a modification to auth.conf
144
145
  # If no key is provided we can only compile the currently connected node.
145
146
  name = request.key || request.node
146
- if node = find_node(name, request.environment)
147
+ if node = find_node(name, request.environment, request.options[:transaction_uuid])
147
148
  return node
148
149
  end
149
150
 
@@ -105,18 +105,17 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
105
105
  result
106
106
 
107
107
  elsif is_http_404?(response)
108
- # 404 gets special treatment as the indirector API can not produce a meaningful
108
+ return nil unless request.options[:fail_on_404]
109
+
110
+ # 404 can get special treatment as the indirector API can not produce a meaningful
109
111
  # reason to why something is not found - it may not be the thing the user is
110
112
  # expecting to find that is missing, but something else (like the environment).
111
- # While this way of handling the issue is not perfect, there is at least a warning
113
+ # While this way of handling the issue is not perfect, there is at least an error
112
114
  # that makes a user aware of the reason for the failure.
113
115
  #
114
116
  content_type, body = parse_response(response)
115
117
  msg = "Find #{uri_with_query_string} resulted in 404 with the message: #{body}"
116
- # warn_once
117
- Puppet::Util::Warnings.maybe_log(msg, self.class){ Puppet.warning msg }
118
- nil
119
-
118
+ raise Puppet::Error, msg
120
119
  else
121
120
  nil
122
121
  end
@@ -3,6 +3,8 @@ class Puppet::Network::HTTP::Route
3
3
  raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError.new("method #{req.method} not allowed for route #{req.path}", Puppet::Network::HTTP::Issues::UNSUPPORTED_METHOD)
4
4
  end
5
5
 
6
+ NO_HANDLERS = [MethodNotAllowedHandler]
7
+
6
8
  attr_reader :path_matcher
7
9
 
8
10
  def self.path(path_matcher)
@@ -12,11 +14,12 @@ class Puppet::Network::HTTP::Route
12
14
  def initialize(path_matcher)
13
15
  @path_matcher = path_matcher
14
16
  @method_handlers = {
15
- :GET => [MethodNotAllowedHandler],
16
- :HEAD => [MethodNotAllowedHandler],
17
- :OPTIONS => [MethodNotAllowedHandler],
18
- :POST => [MethodNotAllowedHandler],
19
- :PUT => [MethodNotAllowedHandler]
17
+ :GET => NO_HANDLERS,
18
+ :HEAD => NO_HANDLERS,
19
+ :OPTIONS => NO_HANDLERS,
20
+ :POST => NO_HANDLERS,
21
+ :PUT => NO_HANDLERS,
22
+ :DELETE => NO_HANDLERS
20
23
  }
21
24
  @chained = []
22
25
  end
@@ -46,6 +49,11 @@ class Puppet::Network::HTTP::Route
46
49
  return self
47
50
  end
48
51
 
52
+ def delete(*handlers)
53
+ @method_handlers[:DELETE] = handlers
54
+ return self
55
+ end
56
+
49
57
  def any(*handlers)
50
58
  @method_handlers.each do |method, registered_handlers|
51
59
  @method_handlers[method] = handlers
@@ -69,7 +77,8 @@ class Puppet::Network::HTTP::Route
69
77
  end
70
78
 
71
79
  def process(request, response)
72
- @method_handlers[request.method.upcase.intern].each do |handler|
80
+ handlers = @method_handlers[request.method.upcase.intern] || NO_HANDLERS
81
+ handlers.each do |handler|
73
82
  handler.call(request, response)
74
83
  end
75
84
 
@@ -36,6 +36,6 @@ scope where the `epp` function is called from.
36
36
  unless Puppet[:parser] == "future"
37
37
  raise ArgumentError, "epp(): function is only available when --parser future is in effect"
38
38
  end
39
- Puppet::Pops::Evaluator::EppEvaluator.epp(self, arguments[0], self.compiler.environment.to_s, arguments[1])
39
+ Puppet::Pops::Evaluator::EppEvaluator.epp(self, arguments[0], self.compiler.environment, arguments[1])
40
40
 
41
41
  end
@@ -530,6 +530,11 @@ class Puppet::Pops::Evaluator::AccessOperator
530
530
  blamed = keys.size == 0 ? @semantic : @semantic.keys[0]
531
531
  keys_orig_size = keys.size
532
532
 
533
+ if keys_orig_size == 0
534
+ fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, blamed,
535
+ :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :max => -1, :actual => 0)
536
+ end
537
+
533
538
  # The result is an array if multiple classnames are given, or if classnames are specified with an array
534
539
  # (possibly multiple arrays, and nested arrays).
535
540
  result_type_array = keys.size > 1 || keys[0].is_a?(Array)
@@ -537,11 +542,6 @@ class Puppet::Pops::Evaluator::AccessOperator
537
542
  keys.flatten!
538
543
  keys.compact!
539
544
 
540
- if keys_orig_size == 0
541
- fail(Puppet::Pops::Issues::BAD_TYPE_SLICE_ARITY, blamed,
542
- :base_type => Puppet::Pops::Types::TypeCalculator.new().string(o), :min => 1, :max => -1, :actual => 0)
543
- end
544
-
545
545
  # If given keys that were just a mix of empty/nil with empty array as a result.
546
546
  # As opposed to calling the function the wrong way (without any arguments), (configurable issue),
547
547
  # Return an empty array
@@ -551,40 +551,45 @@ class Puppet::Pops::Evaluator::AccessOperator
551
551
  return result_type_array ? [] : nil
552
552
  end
553
553
 
554
- if ! o.class_name.nil?
554
+ if o.class_name.nil?
555
+ # The type argument may be a Resource Type - the Puppet Language allows a reference such as
556
+ # Class[Foo], and this is interpreted as Class[Resource[Foo]] - which is ok as long as the resource
557
+ # does not have a title. This should probably be deprecated.
558
+ #
559
+ result = keys.each_with_index.map do |c, i|
560
+ name = if c.is_a?(Puppet::Pops::Types::PResourceType) && !c.type_name.nil? && c.title.nil?
561
+ # type_name is already downcase. Don't waste time trying to downcase again
562
+ c.type_name
563
+ elsif c.is_a?(String)
564
+ c.downcase
565
+ else
566
+ fail(Puppet::Pops::Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], {:name => c})
567
+ end
568
+
569
+ if name =~ Puppet::Pops::Patterns::NAME
570
+ ctype = Puppet::Pops::Types::PHostClassType.new()
571
+ # Remove leading '::' since all references are global, and 3x runtime does the wrong thing
572
+ ctype.class_name = name.sub(/^::/, '')
573
+ ctype
574
+ else
575
+ fail(Issues::ILLEGAL_NAME, @semantic.keys[i], {:name=>c})
576
+ end
577
+ end
578
+ else
555
579
  # lookup class resource and return one or more parameter values
556
580
  resource = find_resource(scope, 'class', o.class_name)
557
- unless resource
558
- fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => 'Class', :title => o.class_name})
559
- end
560
- result = keys.map do |k|
561
- unless is_parameter_of_resource?(scope, resource, k)
562
- fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,
563
- {:type_name => 'Class', :title => o.class_name, :param_name=>k})
581
+ if resource
582
+ result = keys.map do |k|
583
+ if is_parameter_of_resource?(scope, resource, k)
584
+ get_resource_parameter_value(scope, resource, k)
585
+ else
586
+ fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,
587
+ {:type_name => 'Class', :title => o.class_name, :param_name=>k})
588
+ end
564
589
  end
565
- get_resource_parameter_value(scope, resource, k)
566
- end
567
- return result_type_array ? result : result.pop
568
- end
569
-
570
- # The type argument may be a Resource Type - the Puppet Language allows a reference such as
571
- # Class[Foo], and this is interpreted as Class[Resource[Foo]] - which is ok as long as the resource
572
- # does not have a title. This should probably be deprecated.
573
- #
574
- result = keys.each_with_index.map do |c, i|
575
- ctype = Puppet::Pops::Types::PHostClassType.new()
576
- if c.is_a?(Puppet::Pops::Types::PResourceType) && !c.type_name.nil? && c.title.nil?
577
- # Remove leading '::' since all references are global, and 3x runtime does the wrong thing
578
- c = c.type_name.downcase.sub(/^::/, '')
579
- end
580
- unless c.is_a?(String)
581
- fail(Puppet::Pops::Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], {:name => c})
582
- end
583
- if c !~ Puppet::Pops::Patterns::NAME
584
- fail(Issues::ILLEGAL_NAME, @semantic.keys[i], {:name=>c})
590
+ else
591
+ fail(Puppet::Pops::Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => 'Class', :title => o.class_name})
585
592
  end
586
- ctype.class_name = c.downcase.sub(/^::/,'')
587
- ctype
588
593
  end
589
594
 
590
595
  # returns single type as type, else an array of types