right_develop 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # Once this file is required, the Rake DSL is loaded - don't do this except inside Rake!!
24
+ require 'rake/tasklib'
25
+
26
+ # Make sure the rest of RightDevelop & CI is required, since this file can be
27
+ # required directly.
28
+ require 'right_develop'
29
+ require 'right_develop/ci'
30
+
31
+ # Try to load RSpec 2.x - 1.x Rake tasks
32
+ ['rspec/core/rake_task', 'spec/rake/spectask'].each do |f|
33
+ begin
34
+ require f
35
+ rescue LoadError
36
+ # no-op, we will raise later
37
+ end
38
+ end
39
+
40
+ require 'cucumber'
41
+ require 'cucumber/rake/task'
42
+
43
+ module RightDevelop::CI
44
+ # A Rake task definition that creates a CI namespace with appropriate
45
+ # tests.
46
+ class RakeTask < ::Rake::TaskLib
47
+ include ::Rake::DSL if defined?(::Rake::DSL)
48
+
49
+ # The namespace in which to define the continuous integration tasks.
50
+ #
51
+ # Default :ci
52
+ attr_accessor :ci_namespace
53
+
54
+ # File glob to select which specs will be run with the spec task.
55
+ #
56
+ # Default nil (let RSpec choose pattern)
57
+ attr_accessor :rspec_pattern
58
+
59
+ # Filename (without directory!) to which RSpec XML results should be written.
60
+ # The CI task will take output_path, append "rspec" as a subdir and finally
61
+ # append this file name, to come up with a relative path for output. For example:
62
+ #
63
+ # Default "rspec.xml"
64
+ #
65
+ # output_path = "my_cool_ci"
66
+ # rspec_output = "my_awesome_rspec.xml"
67
+ #
68
+ # Given the options above, the CI harness would write RSpec results to:
69
+ # my_cool_ci/rspec/my_awesome_rspec.xml
70
+ attr_accessor :rspec_output
71
+
72
+ # The base directory for all output files.
73
+ #
74
+ # Default 'measurement'
75
+ attr_accessor :output_path
76
+
77
+ def initialize(*args)
78
+ @ci_namespace = args.shift || :ci
79
+
80
+ yield self if block_given?
81
+
82
+ @output_path ||= 'measurement'
83
+ @rspec_output ||= 'rspec.xml'
84
+
85
+ namespace @ci_namespace do
86
+ task :prep do
87
+ FileUtils.mkdir_p(@output_path)
88
+ FileUtils.mkdir_p(File.join(@output_path, 'rspec'))
89
+ FileUtils.mkdir_p(File.join(@output_path, 'cucumber'))
90
+ end
91
+
92
+ if defined?(::RSpec::Core::RakeTask)
93
+ # RSpec 2
94
+ desc "Run RSpec examples"
95
+ RSpec::Core::RakeTask.new(:spec => :prep) do |t|
96
+ t.rspec_opts = ['-r', 'right_develop/ci',
97
+ '-f', JavaSpecFormatter.name,
98
+ '-o', File.join(@output_path, 'rspec', @rspec_output)]
99
+ unless self.rspec_pattern.nil?
100
+ t.pattern = self.rspec_pattern
101
+ end
102
+ end
103
+ elsif defined?(::Spec::Rake::SpecTask)
104
+ # RSpec 1
105
+ Spec::Rake::SpecTask.new(:spec => :prep) do |t|
106
+ desc "Run RSpec Examples"
107
+ t.spec_opts = ['-r', 'right_develop/ci',
108
+ '-f', JavaSpecFormatter.name + ":" + File.join(@output_path, 'rspec', @rspec_output)]
109
+ unless self.rspec_pattern.nil?
110
+ t.spec_files = FileList[self.rspec_pattern]
111
+ end
112
+ end
113
+ else
114
+ raise LoadError, "Cannot define CI rake task: unsupported RSpec version"
115
+ end
116
+
117
+ desc "Run Cucumber features"
118
+ Cucumber::Rake::Task.new do |t|
119
+ t.cucumber_opts = ['--no-color',
120
+ '--format', JavaCucumberFormatter.name,
121
+ '--out', File.join(@output_path, 'cucumber')]
122
+ end
123
+ task :cucumber => [:prep]
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # Try to load RSpec 2.x - 1.x
24
+ ['rspec', 'spec'].each do |f|
25
+ begin
26
+ require f
27
+ rescue LoadError
28
+ # no-op, we will raise later
29
+ end
30
+ end
31
+
32
+ module RightDevelop::Net
33
+ # Extra fatal exceptions to add to RightSupport::Net::RequestBalancer
34
+ FATAL_TEST_EXCEPTIONS = []
35
+
36
+ spec_namespaces = []
37
+
38
+ if defined?(::RSpec::Mocks)
39
+ # RSpec 2.x
40
+ spec_namespaces += [::RSpec::Mocks, ::RSpec::Expectations]
41
+ elsif defined?(::Spec::Expectations)
42
+ # RSpec 1.x
43
+ spec_namespaces += [::Spec::Expectations]
44
+ end
45
+
46
+ # Use some reflection to locate all RSpec and Test::Unit exceptions
47
+ spec_namespaces.each do |namespace|
48
+ namespace.constants.each do |konst|
49
+ konst = namespace.const_get(konst)
50
+ if konst.is_a?(Class) && konst.ancestors.include?(Exception)
51
+ FATAL_TEST_EXCEPTIONS << konst
52
+ end
53
+ end
54
+ end
55
+
56
+ dfe = RightSupport::Net::RequestBalancer::DEFAULT_FATAL_EXCEPTIONS
57
+ FATAL_TEST_EXCEPTIONS.each do |e|
58
+ dfe << e unless dfe.include?(e)
59
+ end
60
+ end
@@ -0,0 +1,10 @@
1
+ # Ensure the main gem is required, since this module might be loaded using ruby -r
2
+ require 'right_develop'
3
+
4
+ module RightDevelop
5
+ module Parsers
6
+ end
7
+ end
8
+
9
+ # Explicitly require everything else to avoid overreliance on autoload (1-module-deep rule)
10
+ require 'right_develop/parsers/sax_parser.rb'
@@ -0,0 +1,139 @@
1
+ require 'xml/libxml'
2
+ require 'active_support/inflector'
3
+ require 'right_develop/parsers/xml_post_parser.rb'
4
+
5
+ module RightDevelop::Parsers
6
+ class SaxParser
7
+ extend XmlPostParser
8
+
9
+ # Parses XML into a ruby hash
10
+ #
11
+ # @param [String] text The XML string to convert into a ruby hash
12
+ # @param [Hash] opts
13
+ # @opts [Lambda or Proc] :post_parser A lambda function to run against
14
+ # the return content of the initial xml parser.
15
+ # @return [Array or Hash] returns rubified XML in Hash and Array format
16
+ def self.parse(text, opts = {})
17
+ # Parse the xml text
18
+ # http://libxml.rubyforge.org/rdoc/
19
+ xml = ::XML::SaxParser::string(text)
20
+ xml.callbacks = new
21
+ xml.parse
22
+
23
+ if opts[:post_parser]
24
+ if opts[:post_parser].kind_of?(Proc)
25
+ return opts[:post_parser].call(xml.callbacks.result)
26
+ else
27
+ raise ArgumentError.new(":post_parser parameter must be a lambda/proc")
28
+ end
29
+ else
30
+ return xml.callbacks.result
31
+ end
32
+ end
33
+
34
+ def initialize
35
+ @tag = {}
36
+ @path = []
37
+ end
38
+
39
+ def result
40
+ @tag
41
+ end
42
+
43
+ # Callbacks
44
+
45
+ def on_error(msg)
46
+ raise msg
47
+ end
48
+
49
+ def on_start_element_ns(name, attr_hash, prefix, uri, namespaces)
50
+ # Push parent tag
51
+ @path << @tag
52
+ # Create a new tag
53
+ if @tag[name]
54
+ @tag[name] = [ @tag[name] ] unless @tag[name].is_a?(Array)
55
+ @tag[name] << {}
56
+ @tag = @tag[name].last
57
+ else
58
+ @tag[name] = {}
59
+ @tag = @tag[name]
60
+ end
61
+ # Put attributes
62
+ attr_hash.each do |key, value|
63
+ @tag["#{key}"] = value
64
+ end
65
+ # Put name spaces
66
+ namespaces.each do |key, value|
67
+ @tag["@xmlns#{key ? ':'+key.to_s : ''}"] = value
68
+ end
69
+ end
70
+
71
+ def on_characters(chars)
72
+ # Ignore lines that contains white spaces only
73
+ return if chars[/\A\s*\z/m]
74
+ # Put Text
75
+ (@tag['@@text'] ||= '') << chars
76
+ end
77
+
78
+ def on_comment(msg)
79
+ # Put Comments
80
+ (@tag['@@comment'] ||= '') << msg
81
+ end
82
+
83
+ def on_end_element_ns(name, prefix, uri)
84
+ # Special handling of empty text fields
85
+ if @tag.is_a?(Hash) && @tag.empty? && @tag['@@text'].nil?
86
+ @tag['@@text'] = nil
87
+ end
88
+
89
+ # Finalize tag's text
90
+ if @tag.keys.count == 0
91
+ # Set tag value to nil then the tag is blank
92
+ name.pluralize == name ? @tag = [] : {}
93
+ elsif @tag.keys == ['@@text']
94
+ # Set tag value to string if it has no any other data
95
+ @tag = @tag['@@text']
96
+ end
97
+ # Make sure we saved the changes
98
+ if @path.last[name].is_a?(Array)
99
+ # If it is an Array then update the very last item
100
+ @path.last[name][-1] = @tag
101
+ else
102
+ # Otherwise just replace the tag
103
+ @path.last[name] = @tag
104
+ end
105
+ # Pop parent tag
106
+ @tag = @path.pop
107
+ end
108
+
109
+ def on_start_document
110
+ end
111
+
112
+ def on_reference (name)
113
+ end
114
+
115
+ def on_processing_instruction(target, data)
116
+ end
117
+
118
+ def on_cdata_block(cdata)
119
+ end
120
+
121
+ def on_has_internal_subset()
122
+ end
123
+
124
+ def on_internal_subset (name, external_id, system_id)
125
+ end
126
+
127
+ def on_is_standalone ()
128
+ end
129
+
130
+ def on_has_external_subset ()
131
+ end
132
+
133
+ def on_external_subset (name, external_id, system_id)
134
+ end
135
+
136
+ def on_end_document
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,113 @@
1
+ require 'active_support/inflector'
2
+
3
+ module RightDevelop::Parsers
4
+ module XmlPostParser
5
+
6
+ # Parses a rubified XML hash/array, removing the top level xml tag, along with
7
+ # any arrays encoded with singular/plural for parent/child nodes.
8
+ # Intended to allow for one set of code for validating JSON or XML responses.
9
+ #
10
+ # @example
11
+ # Initial XML:
12
+ # <top_level>
13
+ # <arrays>
14
+ # <array item1="one" item2="two"/>
15
+ # <array item3="three" item4="four"/>
16
+ # </arrays>
17
+ # </top_level>
18
+ #
19
+ # Before removing nesting, after initial parsing:
20
+ # {
21
+ # 'top_level' => {
22
+ # 'arrays' => {
23
+ # 'array' => [
24
+ # {'item1' => 'one', 'item2' => 'two'},
25
+ # {'item3' => 'three', 'item4' => 'four'}
26
+ # ]
27
+ # }
28
+ # }
29
+ # }
30
+ #
31
+ # After removing nesting:
32
+ # {
33
+ # 'arrays' => [
34
+ # {'item1' => 'one', 'item2' => 'two'},
35
+ # {'item3' => 'three', 'item4' => 'four'}
36
+ # ]
37
+ # }
38
+ #
39
+ # @param [Array or Hash] xml_object parsed XML object, such as from Parser::Sax.parse.
40
+ # @return [Array or Hash] returns a ruby Array or Hash with top level xml tags removed,
41
+ # as well as any extra XML encoded array tags.
42
+ def self.remove_nesting(xml_object)
43
+ if xml_object.length != 1 || (!xml_object.is_a?(Hash) && !xml_object.is_a?(Array))
44
+ raise ArgumentError, "xml_object format doesn't have a single top level entry"
45
+ end
46
+ if !xml_object.is_a?(Hash)
47
+ raise TypeError, "xml_object object doesn't seem to be a Hash or an Array"
48
+ end
49
+ xml_object = deep_clone(xml_object)
50
+
51
+ #if root & children are the same base word, get rid of both layers
52
+ root_key = xml_object.keys[0]
53
+ root_child = xml_object[xml_object.first[0]]
54
+ if root_child.respond_to?(:keys)
55
+ root_child_key = root_child.keys[0]
56
+ else
57
+ root_child_key = nil
58
+ end
59
+
60
+ #remove root key
61
+ xml_object = xml_object[xml_object.first[0]]
62
+
63
+ #remove extra root child key
64
+ if root_key.singularize == root_child_key
65
+ # Ensure object is an array (like JSON responses)
66
+ xml_object = [xml_object[xml_object.first[0]]].flatten
67
+ elsif !xml_object
68
+ # Degenerate case where nothing was contained by parent node
69
+ # (i.e. no resources were actually returned)
70
+ xml_object = []
71
+ end
72
+
73
+ remove_nesting_node(xml_object)
74
+ return xml_object
75
+ end
76
+
77
+ private
78
+
79
+ # Does a deep clone of the passed ruby object. Intended for Arrays / Hashes
80
+ #
81
+ # @param [Object] object to deep clone
82
+ # @return [Object] returns an exact deep copy of the passed in object
83
+ #
84
+ # @example BaseHelpers.deep_clone([{:a=>1},{:b=>2}])
85
+ def self.deep_clone(object)
86
+ Marshal.load(Marshal.dump(object))
87
+ end
88
+
89
+ def self.remove_nesting_node(xml_object_node)
90
+ if xml_object_node.is_a?(Array)
91
+ xml_object_node.each_with_index do |node,index|
92
+ remove_nesting_node(node)
93
+ end
94
+ elsif xml_object_node.is_a?(Hash)
95
+ # If child is singular version of parent, remove extra child pointer
96
+ xml_object_node.each do |parent_key, parent_value|
97
+ if parent_value.is_a?(Hash) && parent_value.length == 1
98
+ child_key = parent_value.keys[0]
99
+ if parent_key.singularize == child_key
100
+ # Wrap xml object in an array so it matches JSON format
101
+ child_node = xml_object_node[parent_key][child_key]
102
+ if child_node.is_a?(Hash)
103
+ child_node = [child_node]
104
+ end
105
+ xml_object_node[parent_key] = child_node
106
+ end
107
+ end
108
+ remove_nesting_node(parent_value)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end