mirage 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.rvmrc +1 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +50 -0
  4. data/README.md +93 -0
  5. data/bin/mirage +54 -0
  6. data/features/checking_for_requests.feature +74 -0
  7. data/features/clearing_requests_and_responses.feature +81 -0
  8. data/features/client/checking_for_requests.feature +20 -0
  9. data/features/client/clearing_responses.feature +75 -0
  10. data/features/client/getting_responses.feature +30 -0
  11. data/features/client/mirage_client.feature +36 -0
  12. data/features/client/peeking.feature +32 -0
  13. data/features/client/setting_responses.feature +91 -0
  14. data/features/client/snapshotting.feature +34 -0
  15. data/features/command_line_iterface.feature +39 -0
  16. data/features/default_responses.feature +91 -0
  17. data/features/file_hosting.feature +8 -0
  18. data/features/logging.feature +7 -0
  19. data/features/peeking_at_response.feature +24 -0
  20. data/features/resources/test.zip +0 -0
  21. data/features/response_templates.feature +45 -0
  22. data/features/root_responses.feature +47 -0
  23. data/features/setting_responses.feature +40 -0
  24. data/features/setting_responses_with_a_delay.feature +10 -0
  25. data/features/setting_responses_with_pattern_matching.feature +72 -0
  26. data/features/snapshotting.feature +25 -0
  27. data/features/step_definitions/my_steps.rb +127 -0
  28. data/features/support/env.rb +89 -0
  29. data/features/web_user_interface.feature +39 -0
  30. data/full_build.sh +100 -0
  31. data/lib/config.ru +5 -0
  32. data/lib/mirage.rb +14 -0
  33. data/lib/mirage/client.rb +140 -0
  34. data/lib/mirage/core.rb +206 -0
  35. data/lib/mirage/util.rb +40 -0
  36. data/lib/mirage/web.rb +65 -0
  37. data/lib/start_mirage.rb +15 -0
  38. data/lib/view/mirage/index.xhtml +23 -0
  39. data/mirage.gemspec +40 -0
  40. data/rakefile +50 -0
  41. metadata +199 -0
@@ -0,0 +1,100 @@
1
+ #!/bin/bash
2
+ blue='\033[34m'
3
+ red='\033[31m'
4
+ green='\033[32m'
5
+ yellow='\033[33m'
6
+ white='\033[37m'
7
+ bold='\033[1m'
8
+ reset='\033[0m'
9
+
10
+ println(){
11
+ echo -e "$1${reset}"
12
+ }
13
+
14
+ print(){
15
+ echo -ne "$1${reset}"
16
+ }
17
+
18
+
19
+
20
+ usage(){
21
+ println "${bold}Usage:\n"
22
+ println "./full_build.sh [ruby_version]\n"
23
+ println "When running with out a ruby version, the full build is run for ruby versions:"
24
+ println "1.8.6\n1.8.7\n1.9.1\n1.9.2\njruby\n"
25
+ println "Else specify a particular ruby version to run the build against\n"
26
+ }
27
+
28
+
29
+ if [ -f "$HOME/.rvm/scripts/rvm" ]
30
+ then
31
+ source "$HOME/.rvm/scripts/rvm"
32
+ elif [ -f "/usr/local/rvm/scripts/rvm" ]
33
+ then
34
+ source "/usr/local/rvm/scripts/rvm"
35
+ else
36
+ println "${bold}RVM Not found"
37
+ println "I looked in $HOME/.rvm/scripts/rvm and /usr/local/rvm/scripts/rvm"
38
+ println "RVM must be installed to run this script. It's great! find out more: here ${bold}http://rvm.beginrescueend.com/"
39
+ println "Until it is installed simply run the default rake target to test Mirage against your active version of Ruby and installed gems"
40
+ exit 1
41
+ fi
42
+
43
+ while getopts ":h" opt; do
44
+ case $opt in
45
+ h)
46
+ usage
47
+ exit 0
48
+ ;;
49
+ \?)
50
+ println "Invalid option: -$OPTARG"
51
+ usage
52
+ exit 1
53
+ ;;
54
+ esac
55
+ done
56
+
57
+ message=""
58
+ result=true
59
+
60
+ run_build_for_ruby( ){
61
+ println "${green}Running build for: $1"
62
+
63
+ ruby_list=`rvm list`
64
+ if [[ ${ruby_list} == *$1* ]]
65
+ then
66
+ rvm --create $1@mirage
67
+ rvm --force gemset empty
68
+ [ -f Gemfile.lock ] && rm Gemfile.lock
69
+ gem install bundler
70
+ bundle install
71
+ rake
72
+
73
+ if [ $? == 0 ]
74
+ then
75
+ message="${message}${blue}$1: ${green}pass\n"
76
+ else
77
+ message="${message}${blue}$1: ${red}fail\n"
78
+ result=false
79
+ fi
80
+ else
81
+ message="${message}${blue}$1: ${yellow}Not installed\n"
82
+ result=false
83
+ fi
84
+ }
85
+
86
+ if [ $1 ]
87
+ then
88
+ run_build_for_ruby $1
89
+ else
90
+ run_build_for_ruby 'ruby-1.8.6'
91
+ run_build_for_ruby 'ruby-1.8.7'
92
+ run_build_for_ruby 'ruby-1.9.1'
93
+ run_build_for_ruby 'ruby-1.9.2'
94
+ run_build_for_ruby 'jruby'
95
+ fi
96
+
97
+ println "\n\n${message}"
98
+ print "${white}Result: "
99
+ [ ${result} == true ] && println "${green}Pass\n" || println "${red}Fail\n"
100
+
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require "#{::File.expand_path("#{::File.dirname(__FILE__)}/mirage/core")}"
3
+ Ramaze.start(:root => __DIR__, :started => true)
4
+ run Ramaze
5
+
@@ -0,0 +1,14 @@
1
+ require 'mirage/web'
2
+ require 'mirage/util'
3
+ require 'mirage/core'
4
+ require 'mirage/client'
5
+
6
+ module Mirage
7
+ def self.default
8
+ yield @@client
9
+ end
10
+
11
+ def self.client= client
12
+ @@client = client
13
+ end
14
+ end
@@ -0,0 +1,140 @@
1
+ require 'uri'
2
+ require 'mechanize'
3
+ require 'open-uri'
4
+ require 'mirage/web'
5
+
6
+ module Mirage
7
+
8
+ class MirageError < ::Exception
9
+ attr_reader :code
10
+
11
+ def initialize message, code
12
+ super message
13
+ @code = message, code
14
+ end
15
+ end
16
+
17
+ class InternalServerException < MirageError;
18
+ end
19
+
20
+ class ResponseNotFound < MirageError;
21
+ end
22
+
23
+ class Client
24
+ include ::Mirage::Web
25
+
26
+ # Creates an instance of the MIrage client that can be used to interact with the Mirage Server
27
+ #
28
+ # Client.new => a client that is configured to connect to Mirage on http://localhost:7001/mirage (the default settings for Mirage)
29
+ # Client.new(URL) => a client that is configured to connect to an instance of Mirage running on the specified url.
30
+ def initialize url="http://localhost:7001/mirage"
31
+ @url = url
32
+ end
33
+
34
+ # Get a response at the given endpoint
35
+ # Mirage::Client.get(endpoint) => response as a string
36
+ # If a response is not found a ResponseNotFound exception is thrown
37
+ #
38
+ # Examples:
39
+ # Getting a response, passing request parameters
40
+ # Mirage::Client.new.get('greeting', :param1 => 'value1', param2=>'value2')
41
+ #
42
+ # Getting a response, passing a content in the body of the request
43
+ # Mirage::Client.new.get('greeting', :body => 'content')
44
+
45
+ def get endpoint, params={}
46
+ response(http_get("#{@url}/get/#{endpoint}", params))
47
+ end
48
+
49
+ # Set a text or file based response, to be hosted at a given end point optionally with a given pattern and delay
50
+ # Client.set(endpoint, params) => unique id that can be used to call back to the server
51
+ #
52
+ # Examples:
53
+ # Client.set('greeting', :response => 'hello':)
54
+ # Client.set('greeting', :response => 'hello', :pattern => 'regex or plain text':)
55
+ # Client.set('greeting', :response => 'hello', :delay => 5) # number of seconds
56
+ def set endpoint, params
57
+ response(http_post("#{@url}/set/#{endpoint}", params))
58
+ end
59
+
60
+ # Use to look at what a response contains without actually triggering it.
61
+ # Client.peek(response_id) => response held on the server as a String
62
+ def peek response_id
63
+ response(http_get("#{@url}/peek/#{response_id}"))
64
+ end
65
+
66
+ # Clear Content from Mirage
67
+ #
68
+ # If a response id is not valid, a ResponseNotFound exception will be thrown
69
+ #
70
+ # Examples:
71
+ # Client.new.clear # clear all responses and associated requests
72
+ # Client.new.clear(response_id) # Clear the response and tracked request for a given response id
73
+ # Client.new.clear(:requests) # Clear all tracked request information
74
+ # Client.new.clear(:request => response_id) # Clear the tracked request for a given response id
75
+ def clear thing=nil
76
+ case thing
77
+ when NilClass then
78
+ http_get("#{@url}/clear")
79
+ when Fixnum then
80
+ http_get("#{@url}/clear/#{thing}")
81
+ when :requests then
82
+ http_get("#{@url}/clear/requests")
83
+ when Hash then
84
+ case thing.keys.first
85
+ when :request then
86
+ http_get("#{@url}/clear/request/#{thing.values.first}")
87
+ end
88
+ end
89
+ end
90
+
91
+
92
+ # Retrieve the last request that triggered a response to be returned. If the request contained content in its body, this is returned. If the
93
+ # request did not have any content in its body then what ever was in the request query string is returned instead
94
+ #
95
+ # Example:
96
+ # Client.new.check(response_id) => Tracked request as a String
97
+ def check response_id
98
+ response(http_get("#{@url}/check/#{response_id}"))
99
+ end
100
+
101
+ # Snapshot the state of the Mirage server so that it can be rolled back to that exact state at a later time.
102
+ def snapshot
103
+ http_post("#{@url}/snapshot").code == 200
104
+ end
105
+
106
+
107
+ # Roll the state of Mirage back to that stored in the snapshot
108
+ # If there is no snapshot to rollback to, nothing happens
109
+ def rollback
110
+ http_post("#{@url}/rollback").code == 200
111
+ end
112
+
113
+
114
+ # Check to see if Mirage is up and running
115
+ def running?
116
+ !http_get(@url).is_a?(Errno::ECONNREFUSED)
117
+ end
118
+
119
+ # Clear down the Mirage Server and load any defaults that are in Mirages default responses directory.
120
+ def load_defaults
121
+ response(http_post("#{@url}/load_defaults"))
122
+ end
123
+
124
+ private
125
+ def response response
126
+ return Mirage::Web::FileResponse.new(response) if response.instance_of?(Mechanize::File)
127
+ case response.code
128
+ when 500 then
129
+ raise ::Mirage::InternalServerException.new(response.page.body, response.code)
130
+ when 404 then
131
+ raise ::Mirage::ResponseNotFound.new(response.page.body, response.code)
132
+ else
133
+ response.body
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+
140
+ end
@@ -0,0 +1,206 @@
1
+ require 'ramaze'
2
+ require 'ramaze/helper/send_file'
3
+
4
+ class Object
5
+ def deep_clone
6
+ Marshal.load(Marshal.dump(self))
7
+ end
8
+ end
9
+
10
+ module Mirage
11
+
12
+ class MockResponse
13
+ @@id_count = 0
14
+ attr_reader :response_id, :delay, :name, :pattern
15
+ attr_accessor :response_id
16
+
17
+ def initialize name, value, pattern=nil, delay=0, root_response=false
18
+ @name, @value, @pattern, @response_id, @delay, @root_response = name, value, pattern, @@id_count+=1, delay, root_response
19
+ end
20
+
21
+ def self.reset_count
22
+ @@id_count = 0
23
+ end
24
+
25
+ def root_response?
26
+ @root_response
27
+ end
28
+
29
+ def file?
30
+ !@value.is_a?(String)
31
+ end
32
+
33
+
34
+ def value(body='', request_parameters={}, query_string='')
35
+ return @value if file?
36
+
37
+ value = @value
38
+ value.scan(/\$\{(.*)?\}/).flatten.each do |pattern|
39
+
40
+ if (parameter_match = request_parameters[pattern])
41
+ value = value.gsub("${#{pattern}}", parameter_match)
42
+ end
43
+
44
+ [body, query_string].each do |string|
45
+ if (string_match = find_match(string, pattern))
46
+ value = value.gsub("${#{pattern}}", string_match)
47
+ end
48
+ end
49
+
50
+ end
51
+ value
52
+ end
53
+
54
+ private
55
+ def find_match(string, regex)
56
+ string.scan(/#{regex}/).flatten.first
57
+ end
58
+ end
59
+
60
+
61
+ class MirageServer < Ramaze::Controller
62
+ include Ramaze::Helper::SendFile
63
+ map '/mirage'
64
+ RESPONSES, REQUESTS, SNAPSHOT= {}, {}, {}
65
+
66
+ def index
67
+ @responses = {}
68
+
69
+ RESPONSES.each do |name, responses|
70
+ @responses[name]=responses.default unless responses.default.nil?
71
+
72
+ responses.each do |pattern, response|
73
+ @responses["#{name}#{'/*' if response.root_response?}: #{pattern}"] = response
74
+ end
75
+ end
76
+ end
77
+
78
+ def peek response_id
79
+ peeked_response = nil
80
+ RESPONSES.values.each do |responses|
81
+ peeked_response = responses[:default] if responses[:default] && responses[:default].response_id == response_id.to_i
82
+ peeked_response = responses.values.find { |response| response.response_id == response_id.to_i } if peeked_response.nil?
83
+ break unless peeked_response.nil?
84
+ end
85
+ respond("Can not peek reponse, id:#{response_id} does not exist}", 404) unless peeked_response
86
+ send_response(peeked_response)
87
+ end
88
+
89
+ def set *args
90
+ delay = (request['delay']||0)
91
+ pattern = request['pattern'] ? /#{request['pattern']}/ : :default
92
+ name = args.join('/')
93
+ is_root_response = request['root_response'] == 'true'
94
+
95
+ response = MockResponse.new(name, (request[:file]||response_value), pattern, delay.to_f, is_root_response)
96
+
97
+ stored_responses = RESPONSES[name]||={}
98
+
99
+ old_response = stored_responses.delete(pattern)
100
+ stored_responses[pattern] = response
101
+
102
+ # Right not an the main id count goes up by one even if the id is not used because the old id is reused from another response
103
+ response.response_id = old_response.response_id if old_response
104
+ response.response_id
105
+ end
106
+
107
+ def get *args
108
+ body, query_string = Rack::Utils.unescape(request.body.read.to_s), request.env['QUERY_STRING']
109
+ name = args.join('/')
110
+ stored_responses = RESPONSES[name]
111
+
112
+ if stored_responses
113
+ record = find_response(body, query_string, stored_responses)
114
+ else
115
+ root_responses, record = find_root_responses(name), nil
116
+
117
+ until record || root_responses.empty?
118
+ record = find_response(body, query_string, root_responses.delete_at(0))
119
+ record = record.root_response? ? record : nil
120
+ end
121
+ end
122
+
123
+ respond('Response not found', 404) unless record
124
+ REQUESTS[record.response_id] = body.empty? ? query_string : body
125
+
126
+ sleep record.delay
127
+ send_response(record, body, request, query_string)
128
+ end
129
+
130
+ def clear datatype=nil, response_id=nil
131
+ response_id = response_id.to_i
132
+ case datatype
133
+ when 'requests' then
134
+ REQUESTS.clear
135
+ when 'responses' then
136
+ RESPONSES.clear and REQUESTS.clear and MockResponse.reset_count
137
+ when /\d+/ then
138
+ response_id = datatype.to_i
139
+ delete_response(response_id)
140
+ REQUESTS.delete(response_id)
141
+ when 'request'
142
+ REQUESTS.delete(response_id)
143
+ when nil
144
+ [REQUESTS, RESPONSES].each { |map| map.clear }
145
+ MockResponse.reset_count
146
+ end
147
+ end
148
+
149
+ def check id
150
+ REQUESTS[id.to_i] || respond("Nothing stored for: #{id}", 404)
151
+ end
152
+
153
+ def snapshot
154
+ SNAPSHOT.clear and SNAPSHOT.replace(RESPONSES.deep_clone)
155
+ end
156
+
157
+ def rollback
158
+ RESPONSES.clear and RESPONSES.replace(SNAPSHOT.deep_clone)
159
+ end
160
+
161
+ def load_defaults
162
+ clear
163
+ Dir["#{DEFAULT_RESPONSES_DIR}/**/*.rb"].each do |default|
164
+ begin
165
+ load default
166
+ rescue Exception
167
+ respond("Unable to load default responses from: #{default}", 500)
168
+ end
169
+
170
+ end
171
+ end
172
+
173
+ private
174
+ def find_response(body, query_string, stored_responses)
175
+ pattern_match = stored_responses.keys.find_all { |pattern| pattern != :default }.find { |pattern| body =~ pattern || query_string =~ pattern }
176
+ record = pattern_match ? stored_responses[pattern_match] : stored_responses[:default]
177
+ record
178
+ end
179
+
180
+ def response_value
181
+ return request['response'] unless request['response'].nil?
182
+ respond('response or file parameter required', 500)
183
+ end
184
+
185
+ def find_root_responses(name)
186
+ matches = RESPONSES.keys.find_all { |key| name.index(key) == 0 }.sort { |a, b| b.length <=> a.length }
187
+ matches.collect { |key| RESPONSES[key] }
188
+ end
189
+
190
+ def delete_response(response_id)
191
+ RESPONSES.each do |name, response_set|
192
+ response_set.each { |key, response| response_set.delete(key) if response.response_id == response_id }
193
+ end
194
+ end
195
+
196
+ def send_response(response, body='', request={}, query_string='')
197
+ if response.file?
198
+ tempfile, filename, type = response.value.values_at(:tempfile, :filename, :type)
199
+ send_file(tempfile.path, type, "Content-Disposition: attachment; filename=#{filename}")
200
+ else
201
+ response.value(body, request, query_string)
202
+ end
203
+ end
204
+
205
+ end
206
+ end