js-client-bridge 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/Manifest.txt +21 -0
- data/README.txt +186 -0
- data/Rakefile +74 -0
- data/lib/js-client-bridge.rb +14 -0
- data/lib/js-client-bridge/extensions/datamapper_errors.rb +43 -0
- data/lib/js-client-bridge/responses.rb +47 -0
- data/lib/js-client-bridge/responses/error.rb +22 -0
- data/lib/js-client-bridge/responses/exception.rb +60 -0
- data/lib/js-client-bridge/responses/ok.rb +21 -0
- data/lib/js-client-bridge/responses/validation.rb +47 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/error_responses_spec.rb +23 -0
- data/spec/exception_responses_spec.rb +56 -0
- data/spec/ok_responses_spec.rb +46 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/validation_responses_spec.rb +72 -0
- data/tasks/rspec.rake +21 -0
- metadata +151 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/js-client-bridge.rb
|
6
|
+
lib/js-client-bridge/responses.rb
|
7
|
+
lib/js-client-bridge/responses/ok.rb
|
8
|
+
lib/js-client-bridge/responses/validation.rb
|
9
|
+
lib/js-client-bridge/responses/error.rb
|
10
|
+
lib/js-client-bridge/responses/exception.rb
|
11
|
+
lib/js-client-bridge/extensions/datamapper_errors.rb
|
12
|
+
script/console
|
13
|
+
script/destroy
|
14
|
+
script/generate
|
15
|
+
spec/ok_responses_spec.rb
|
16
|
+
spec/error_responses_spec.rb
|
17
|
+
spec/exception_responses_spec.rb
|
18
|
+
spec/validation_responses_spec.rb
|
19
|
+
spec/spec.opts
|
20
|
+
spec/spec_helper.rb
|
21
|
+
tasks/rspec.rake
|
data/README.txt
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
= js-client-bridge
|
2
|
+
|
3
|
+
* https://github.com/luma/js-client-bridge/tree/master
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Little library that encapsulates a (particular) standardised way of talking between a service and javascript. Probably not the best way of doing things, but it's been handy in a pinch.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* TODO
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
=== Standard and Custom Fields
|
16
|
+
Any fields that begin with a '_' are standard fields that will be used among all the different response types. These fields are quite often required or will have specific set behavior that the client side is expected to follow. Any fields that don't begin with '_' are custom fields and the client side can use them (or not) however they want.
|
17
|
+
|
18
|
+
=== Use of JSON With Padding (JSONP)
|
19
|
+
We support JSONP (see [http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/ Here] for an introduction to JSONP). Meaning that, rather than the standard JSON responses below:
|
20
|
+
|
21
|
+
{
|
22
|
+
_status : 'ok',
|
23
|
+
_message : 'a status message'
|
24
|
+
}
|
25
|
+
|
26
|
+
|
27
|
+
A service may be required to return a response of the form:
|
28
|
+
|
29
|
+
jsonp_identifier_12321({
|
30
|
+
_status : 'ok',
|
31
|
+
_message : 'a status message'
|
32
|
+
} )
|
33
|
+
|
34
|
+
|
35
|
+
Whether JSONP is used for a response or not is decided by the calling client and services should support either response format.
|
36
|
+
|
37
|
+
|
38
|
+
=== Everything's Cool Response
|
39
|
+
This is returned when a remote call succeeds, it must include the status of 'ok'. It can optionally include a message, if a message is included it should be displayed to the user.
|
40
|
+
|
41
|
+
|
42
|
+
==== Required Fields
|
43
|
+
[_status] String - This will always be 'ok'.
|
44
|
+
|
45
|
+
|
46
|
+
==== Optional Fields
|
47
|
+
[_message] String - If sent from server-side client side should display it.
|
48
|
+
|
49
|
+
==== Example
|
50
|
+
{
|
51
|
+
_status : 'ok',
|
52
|
+
_message : 'a status message'
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
=== A General Error Occurred
|
57
|
+
This is returned when a remote call fails with a general error, where in this case general error means we want to display feedback to the user but we want to leave any further action up to the client side. It can optionally include a message, if a message is included it should be displayed to the user.
|
58
|
+
|
59
|
+
|
60
|
+
==== Required Fields
|
61
|
+
[_status] String - This will always be 'error'.
|
62
|
+
|
63
|
+
==== Optional Fields
|
64
|
+
[_message] String - If sent from server-side client side should display it.
|
65
|
+
|
66
|
+
==== Example
|
67
|
+
{
|
68
|
+
_status : 'error',
|
69
|
+
_message : 'asdfasfasd'
|
70
|
+
}
|
71
|
+
|
72
|
+
=== An Unexpected Exception Occurred
|
73
|
+
This is returned when something explodes on the server side. These errors are assumed to be unhandlable on the client side (due to the absurdly large number of possible errors). The client side should take the error info and display it to the user in a standard format. During development or testing this should include displaying all the information on the screen. What happens in production is an open question.
|
74
|
+
|
75
|
+
|
76
|
+
==== Required Fields
|
77
|
+
[_status] String - This will always be 'error'.
|
78
|
+
[_type] String - This will be the fully namespaced name of the exception.
|
79
|
+
[_short_type] String - This will be the short, easily readable version of _type.
|
80
|
+
[request_uri] String - The uri of the original request that triggered this exception.
|
81
|
+
[parameters] Hash - A hash of key/value pairs representing the query string or POSTed data. If there are none, an empty hash ({}) should be used.
|
82
|
+
|
83
|
+
==== Optional Fields
|
84
|
+
[_message] String - If sent from server-side client side should display it.
|
85
|
+
|
86
|
+
==== Example
|
87
|
+
{
|
88
|
+
_status : "exception",
|
89
|
+
_type : "Merb::Controller::Exceptions::InternalServerError",
|
90
|
+
_short_type : "InternalServerError",
|
91
|
+
request_uri : '/asdf,
|
92
|
+
parameters : {name : value, name : value, name : value},
|
93
|
+
exceptions : [
|
94
|
+
{
|
95
|
+
name : 'Twimmy::Exceptions::ServerError',
|
96
|
+
message : 'A awful error occured',
|
97
|
+
backtrace : [
|
98
|
+
"file:line",
|
99
|
+
"file:line",
|
100
|
+
"file:line",
|
101
|
+
"file:line"
|
102
|
+
]
|
103
|
+
]
|
104
|
+
}
|
105
|
+
|
106
|
+
=== Validation for the Request Failed
|
107
|
+
This response is returned when server side validation of an object failed, it should include enough information to display dynamic validation messages and highlight/animate input fields. It is suggested that to make this work seamlessly there must be some way to map html field ids to attribute names in a way that won't clash. One way that is quite efficient is to use form element ids of the form:
|
108
|
+
'''short_object_type-object_id-attribute_name'''
|
109
|
+
|
110
|
+
Or if the object is not yet created and doesn't have an id:
|
111
|
+
'''short_object_type-attribute_name'''
|
112
|
+
|
113
|
+
For example, if we were calling a remote service to update an Offer with the id of 1, and validation failed on the bid_amount field then the attribute mapping would be:
|
114
|
+
'''offer-1-bid_amount'''
|
115
|
+
|
116
|
+
|
117
|
+
==== Example
|
118
|
+
The advantage of this scheme is that dynamic validation UI bits can be applied to a client side form using something like the following (this is pseudo code, it's untested):
|
119
|
+
|
120
|
+
var response = get_response_from_server_side();
|
121
|
+
|
122
|
+
jQuery.each(response.validation, function(field_name, errors) {
|
123
|
+
var field_id = [response._short_type, response.id, field_name].join('-');
|
124
|
+
$('#' + field_id).highlight().shake().addClass('error').after("<strong class='form-error'>" + errors.join(', ') + "</strong>");
|
125
|
+
});
|
126
|
+
|
127
|
+
==== Required Fields
|
128
|
+
[_status] String - This will always be 'exception'.
|
129
|
+
[_type] String - This will be the fully namespaced name of the exception.
|
130
|
+
[_short_type] String - This will be the short, easily readable version of _type.
|
131
|
+
[validation] Has - The keys of the hash are the field name on which the error occured. these should has no spaces. The values are arrays of strings, where each string is an error message.
|
132
|
+
|
133
|
+
|
134
|
+
=== Optional Fields
|
135
|
+
[_message] String - If sent from server-side client side should display it.
|
136
|
+
[id] String - The server side id of the object.
|
137
|
+
|
138
|
+
=== Example
|
139
|
+
{
|
140
|
+
"_status" => "validation",
|
141
|
+
"_type" => "Twimmy::Test::TestDO",
|
142
|
+
"_short_type" => "TestDO",
|
143
|
+
"_message" => "Sorry, we couldn't save your TestDO",
|
144
|
+
"action" => "create",
|
145
|
+
"validation" : {
|
146
|
+
"body" : ["Body must not be blank"],
|
147
|
+
"subject" : ["Subject must not be blank", "Subject must be between 30 and 155 characters long"],
|
148
|
+
"id" : ["Id must not be blank"]
|
149
|
+
},
|
150
|
+
}
|
151
|
+
|
152
|
+
== REQUIREMENTS:
|
153
|
+
|
154
|
+
* json gem
|
155
|
+
* extlib gem
|
156
|
+
|
157
|
+
== INSTALL:
|
158
|
+
The first line add's github as a source for your local rubygems.
|
159
|
+
|
160
|
+
sudo gem sources -a http://gems.github.com
|
161
|
+
sudo gem install luma-js-client-bridge
|
162
|
+
|
163
|
+
== LICENSE:
|
164
|
+
|
165
|
+
(The MIT License)
|
166
|
+
|
167
|
+
Copyright (c) 2009 Rolly
|
168
|
+
|
169
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
170
|
+
a copy of this software and associated documentation files (the
|
171
|
+
'Software'), to deal in the Software without restriction, including
|
172
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
173
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
174
|
+
permit persons to whom the Software is furnished to do so, subject to
|
175
|
+
the following conditions:
|
176
|
+
|
177
|
+
The above copyright notice and this permission notice shall be
|
178
|
+
included in all copies or substantial portions of the Software.
|
179
|
+
|
180
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
181
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
182
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
183
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
184
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
185
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
186
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
=begin
|
2
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
3
|
+
require File.dirname(__FILE__) + '/lib/js-client-bridge'
|
4
|
+
|
5
|
+
# Generate all the Rake tasks
|
6
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
7
|
+
$hoe = Hoe.new('js-client-bridge', JsClientBridge::VERSION) do |p|
|
8
|
+
p.developer('Rolly', 'rolly@luma.co.nz')
|
9
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
10
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
11
|
+
p.rubyforge_name = p.name # TODO this is default value
|
12
|
+
p.extra_deps = [
|
13
|
+
# ['activesupport','>= 2.0.2'],
|
14
|
+
['json', '>= 0'],
|
15
|
+
['extlib', '>= 0']
|
16
|
+
]
|
17
|
+
p.extra_dev_deps = [
|
18
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
19
|
+
]
|
20
|
+
|
21
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
22
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
23
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
24
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
28
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
29
|
+
|
30
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
31
|
+
# task :default => [:spec, :features]
|
32
|
+
|
33
|
+
desc "Run all examples (or a specific spec with TASK=xxxx)"
|
34
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
35
|
+
t.spec_opts = ["-cfs"]
|
36
|
+
t.spec_files = begin
|
37
|
+
if ENV["TASK"]
|
38
|
+
ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
|
39
|
+
else
|
40
|
+
FileList['spec/**/*_spec.rb']
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#desc 'Default: run spec examples'
|
46
|
+
#task :default => 'spec'
|
47
|
+
=end
|
48
|
+
# -*- ruby -*-
|
49
|
+
|
50
|
+
require 'rubygems'
|
51
|
+
require 'hoe'
|
52
|
+
require 'fileutils'
|
53
|
+
require 'newgem'
|
54
|
+
require 'rubigen'
|
55
|
+
|
56
|
+
#require File.dirname(__FILE__) + '/lib/js-client-bridge'
|
57
|
+
|
58
|
+
Hoe.spec 'js-client-bridge' do
|
59
|
+
developer 'Rolly', 'rolly@luma.co.nz'
|
60
|
+
|
61
|
+
extra_deps << ['json', '>= 0']
|
62
|
+
extra_deps << ['extlib', '>= 0']
|
63
|
+
|
64
|
+
# extra_dev_deps << ['newgem', ">= #{::Newgem::VERSION}"]
|
65
|
+
=begin
|
66
|
+
clean_globs |= %w[**/.DS_Store tmp *.log]
|
67
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
68
|
+
remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
69
|
+
rsync_args = '-av --delete --ignore-errors'
|
70
|
+
=end
|
71
|
+
end
|
72
|
+
|
73
|
+
# vim: syntax=ruby
|
74
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module JsClientBridge
|
5
|
+
VERSION = '0.1.0'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'json'
|
10
|
+
require "extlib"
|
11
|
+
require 'hoe'
|
12
|
+
|
13
|
+
require 'js-client-bridge/responses'
|
14
|
+
require 'js-client-bridge/extensions/datamapper_errors'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module DataMapper
|
3
|
+
module Validate
|
4
|
+
|
5
|
+
##
|
6
|
+
#
|
7
|
+
# Monkey patch of Datamapper to add a nice to_json method that uses our error format
|
8
|
+
class ValidationErrors
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
js = {}
|
12
|
+
|
13
|
+
errors.each do |list, pair|
|
14
|
+
js[list] = pair
|
15
|
+
end
|
16
|
+
|
17
|
+
js
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class ValidationErrors
|
21
|
+
end # module Validate
|
22
|
+
end # module DataMapper
|
23
|
+
|
24
|
+
module DataMapper
|
25
|
+
module Resource
|
26
|
+
def errors_to_hash(message = "Sorry, we couldn't save your #{self.class.to_s.split('::').last}")
|
27
|
+
|
28
|
+
short_type = self.class.to_s.split('::').last
|
29
|
+
|
30
|
+
validation = {
|
31
|
+
:_status => 'validation',
|
32
|
+
:_type => self.class,
|
33
|
+
:_short_type => short_type,
|
34
|
+
:_message => message,
|
35
|
+
:validation => self.errors.to_hash,
|
36
|
+
}
|
37
|
+
|
38
|
+
validation[:id] = self.id.to_s unless new_record?
|
39
|
+
|
40
|
+
validation
|
41
|
+
end
|
42
|
+
end # module Resource
|
43
|
+
end # module DataMapper
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
base = File.dirname(__FILE__) / 'responses'
|
4
|
+
%w(ok error exception validation).each {|f| require base / f }
|
5
|
+
|
6
|
+
module JSClientBridge #:nodoc:
|
7
|
+
module Responses
|
8
|
+
class << self
|
9
|
+
include Responses::Ok
|
10
|
+
include Responses::Error
|
11
|
+
include Responses::Exception
|
12
|
+
include Responses::Validation
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def formatting_parameters
|
17
|
+
[:jsonp]
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_with(type, args)
|
21
|
+
unless args.blank?
|
22
|
+
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
23
|
+
options[:_message] = args.shift.dup if args.first.is_a?(String)
|
24
|
+
else
|
25
|
+
options = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# separate out formatting parameters
|
29
|
+
formatting = {}
|
30
|
+
for para in formatting_parameters
|
31
|
+
formatting[para] = options.delete(para) if options.include?(para)
|
32
|
+
end
|
33
|
+
|
34
|
+
[options.merge({ :_status => type.to_s }), formatting]
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_response(response, formatting_options = {})
|
38
|
+
# Handle JSONP responses
|
39
|
+
if formatting_options[:jsonp].blank?
|
40
|
+
response.to_json
|
41
|
+
else
|
42
|
+
"#{formatting_options[:jsonp]}(#{response.to_json})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end # module Responses
|
47
|
+
end # module JSClientBridge
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module JSClientBridge #:nodoc:
|
4
|
+
module Responses #:nodoc:
|
5
|
+
module Error
|
6
|
+
# Generates a error response. If the first parameter is a string is will be
|
7
|
+
# used as the _status options. It will also honour custom optionss as long
|
8
|
+
# they don't clash with the standard ones.
|
9
|
+
#
|
10
|
+
# ==== Parameters
|
11
|
+
# message<String>:: An optional message.
|
12
|
+
# options<Hash>:: Custom optionss.
|
13
|
+
#
|
14
|
+
# ==== Returns
|
15
|
+
# JSON String::
|
16
|
+
def respond_with_error(*args)
|
17
|
+
format_response(*respond_with('error', args))
|
18
|
+
end
|
19
|
+
|
20
|
+
end # module Error
|
21
|
+
end # module Responses
|
22
|
+
end # module JSClientBridge
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module JSClientBridge #:nodoc:
|
4
|
+
module Responses #:nodoc:
|
5
|
+
module Exception
|
6
|
+
|
7
|
+
def respond_with_exception(*args)
|
8
|
+
raise ArgumentError.new("respond_with_exception requires an exception as it's first parameter") if args.blank? || !args.first.is_a?(StandardError)
|
9
|
+
|
10
|
+
exception = args.shift
|
11
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
12
|
+
|
13
|
+
# Ensure we have our required fields
|
14
|
+
raise ArgumentError.new("respond_with_exception requires the request_uri option") unless options.include?(:request_uri)
|
15
|
+
|
16
|
+
# Set some defaults
|
17
|
+
options[:parameters] ||= {}
|
18
|
+
|
19
|
+
# Generate our response hash and add the exceptions parameter
|
20
|
+
response, formatting = respond_with('exception', [options])
|
21
|
+
response[:exceptions] = [{
|
22
|
+
:name => exception.class,
|
23
|
+
:short_name => exception.class.to_s.split('::').last,
|
24
|
+
:message => CGI.escape(exception.message),
|
25
|
+
:backtrace => exception.backtrace
|
26
|
+
}]
|
27
|
+
|
28
|
+
format_response(response, formatting)
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_with_exceptions(*args)
|
32
|
+
raise ArgumentError.new("respond_with_exception requires an array of exceptions as it's first parameter") if args.blank? || !args.first.is_a?(Array)
|
33
|
+
|
34
|
+
exceptions = args.shift
|
35
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
36
|
+
|
37
|
+
# Ensure we have our required fields
|
38
|
+
raise ArgumentError.new("respond_with_exception requires the request_uri option") unless options.include?(:request_uri)
|
39
|
+
|
40
|
+
# Set some defaults
|
41
|
+
options[:parameters] ||= {}
|
42
|
+
|
43
|
+
# Generate our response hash and add the exceptions parameter
|
44
|
+
response, formatting = respond_with('exception', [options])
|
45
|
+
|
46
|
+
response[:exceptions] = exceptions.map do |exception|
|
47
|
+
{
|
48
|
+
:name => exception.class,
|
49
|
+
:short_name => exception.class.to_s.split('::').last,
|
50
|
+
:message => CGI.escape(exception.message),
|
51
|
+
:backtrace => exception.backtrace
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
format_response(response, formatting)
|
56
|
+
end
|
57
|
+
|
58
|
+
end # module Exception
|
59
|
+
end # module Responses
|
60
|
+
end # module JSClientBridge
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module JSClientBridge #:nodoc:
|
4
|
+
module Responses #:nodoc:
|
5
|
+
module Ok
|
6
|
+
# Generates a 'ok' status response. If the first parameter is a string is will be
|
7
|
+
# used as the _status options. It will also honour custom optionss as long
|
8
|
+
# they don't clash with the standard ones.
|
9
|
+
#
|
10
|
+
# ==== Parameters
|
11
|
+
# message<String>:: An optional message.
|
12
|
+
# options<Hash>:: Custom optionss.
|
13
|
+
#
|
14
|
+
# ==== Returns
|
15
|
+
# JSON String::
|
16
|
+
def respond_with_ok(*args)
|
17
|
+
format_response(*respond_with('ok', args))
|
18
|
+
end
|
19
|
+
end # module Ok
|
20
|
+
end # module Responses
|
21
|
+
end # module JSClientBridge
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module JSClientBridge #:nodoc:
|
4
|
+
module Responses #:nodoc:
|
5
|
+
module Validation
|
6
|
+
# Generates a 'ok' status response. If the first parameter is a string is will be
|
7
|
+
# used as the _status options. It will also honour custom optionss as long
|
8
|
+
# they don't clash with the standard ones.
|
9
|
+
#
|
10
|
+
# ==== Parameters
|
11
|
+
# message<String>:: An optional message.
|
12
|
+
# options<Hash>:: Custom optionss.
|
13
|
+
#
|
14
|
+
# ==== Returns
|
15
|
+
# JSON String::
|
16
|
+
def respond_with_validation_error(*args)
|
17
|
+
if args.blank? || !args.first.respond_to?(:errors_to_hash)
|
18
|
+
raise ArgumentError.new("respond_with_validation_error requires an object that responds to errors_to_hash as it's first parameter")
|
19
|
+
end
|
20
|
+
|
21
|
+
obj = args.shift
|
22
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
23
|
+
|
24
|
+
# Generate our response hash and add the exceptions parameter
|
25
|
+
response = if options.include?(:message)
|
26
|
+
options.merge(obj.errors_to_hash(options.delete(:message)))
|
27
|
+
|
28
|
+
else
|
29
|
+
options.merge(obj.errors_to_hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
format_response(response, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
end # module Validation
|
36
|
+
end # module Responses
|
37
|
+
end # module JSClientBridge
|
38
|
+
|
39
|
+
=begin
|
40
|
+
{
|
41
|
+
:_status => 'validation',
|
42
|
+
:_type => self.class,
|
43
|
+
:_short_type => self.class.to_s.split('::').first,
|
44
|
+
:_message => message,
|
45
|
+
:validation => self.errors.to_json
|
46
|
+
}
|
47
|
+
=end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/js-client-bridge.rb'}"
|
9
|
+
puts "Loading js-client-bridge gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "Error Responses" do
|
4
|
+
it "should generate a basic error response" do
|
5
|
+
JSClientBridge::Responses.respond_with_error.should == {:_status => 'error'}.to_json
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should generate a error response with a status message" do
|
9
|
+
JSClientBridge::Responses.respond_with_error('a message').should == {:_status => 'error', :_message => "a message"}.to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should generate a error response with a status message and additional custom properties" do
|
13
|
+
JSClientBridge::Responses.respond_with_error('a message', :blame => 'the user').should == {:_status => 'error', :_message => "a message", :blame => 'the user'}.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should generate a error response with custom properties but not message" do
|
17
|
+
JSClientBridge::Responses.respond_with_error(:blame => 'the user').should == {:_status => 'error', :blame => 'the user'}.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should generate a basic error response using JSONP" do
|
21
|
+
JSClientBridge::Responses.respond_with_error('a message', :jsonp => 'jsonp1').should == "jsonp1(#{{:_status => 'error', :_message => "a message"}.to_json})"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
class TestException < StandardError; end
|
6
|
+
|
7
|
+
describe "Exception Responses" do
|
8
|
+
|
9
|
+
it "should generate an exception response" do
|
10
|
+
exception = TestException.new('An exception')
|
11
|
+
|
12
|
+
ex = JSON.parse(JSClientBridge::Responses.respond_with_exception(exception, :request_uri => '/', :parameters => {:param1 => 'value1'}))
|
13
|
+
|
14
|
+
ex['_status'].should == 'exception'
|
15
|
+
ex['request_uri'].should == '/'
|
16
|
+
ex['parameters'].should == {'param1' => 'value1'}
|
17
|
+
ex['exceptions'].length.should == 1
|
18
|
+
|
19
|
+
e = ex['exceptions'].first
|
20
|
+
e['name'].should == exception.class.to_s
|
21
|
+
e['message'].should == CGI.escape(exception.message)
|
22
|
+
e['backtrace'].should == exception.backtrace
|
23
|
+
e['short_name'].should == exception.class.to_s.split('::').last
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should generate an exceptions response" do
|
27
|
+
exceptions = [TestException.new('An exception'), TestException.new('Another exception')]
|
28
|
+
|
29
|
+
ex = JSON.parse(JSClientBridge::Responses.respond_with_exceptions(exceptions, :request_uri => '/', :parameters => {:param1 => 'value1'}))
|
30
|
+
|
31
|
+
ex['_status'].should == 'exception'
|
32
|
+
ex['request_uri'].should == '/'
|
33
|
+
ex['parameters'].should == {'param1' => 'value1'}
|
34
|
+
ex['exceptions'].length.should == 2
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should require the request_uri option" do
|
38
|
+
lambda {
|
39
|
+
JSClientBridge::Responses.respond_with_exception(TestException.new('An exception'), :parameters => {:param1 => 'value1'})
|
40
|
+
}.should raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should require the exception option" do
|
44
|
+
lambda {
|
45
|
+
JSClientBridge::Responses.respond_with_exception(:request_uri => '/', :parameters => {:param1 => 'value1'})
|
46
|
+
}.should raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should generate an exception response using JSONP" do
|
50
|
+
exception = TestException.new('An exception')
|
51
|
+
|
52
|
+
ex = JSClientBridge::Responses.respond_with_exception(exception, :request_uri => '/', :parameters => {:param1 => 'value1'}, :jsonp => 'jsonp1')
|
53
|
+
ex[0..6].should == 'jsonp1('
|
54
|
+
ex[-1..-1].should == ')'
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
describe "Ok Responses" do
|
7
|
+
it "should generate a basic ok response" do
|
8
|
+
JSClientBridge::Responses.respond_with_ok.should == {:_status => 'ok'}.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should generate a ok response with a status message" do
|
12
|
+
JSClientBridge::Responses.respond_with_ok('a message').should == {:_status => 'ok', :_message => "a message"}.to_json
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should generate a ok response with a status message and additional custom parameters" do
|
16
|
+
JSClientBridge::Responses.respond_with_ok('a message', :awesomeness => 'has happened').should == {:_status => 'ok', :_message => "a message", :awesomeness => 'has happened'}.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should generate a ok response with a status message and a complex parameters" do
|
20
|
+
JSClientBridge::Responses.respond_with_ok('a message', :stuff => complext_test_data).should == {:_message => "a message", :stuff => complext_test_data, :_status => 'ok'}.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should generate a basic ok response using JSONP" do
|
24
|
+
JSClientBridge::Responses.respond_with_ok('a message', :jsonp => 'jsonp1').should == "jsonp1(#{{:_status => 'ok', :_message => "a message"}.to_json})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def complext_test_data
|
28
|
+
[
|
29
|
+
{:name=>"Whangarei",
|
30
|
+
:description=>"Northland Coach & Travel Centre, 3C Bank St.",
|
31
|
+
:id=>:WRE},
|
32
|
+
{:name=>"Westwood Lodge", :description=>"Westwood Lodge", :id=>:WWL},
|
33
|
+
{:name=>"Queenstown YHA Lake Esplanade",
|
34
|
+
:description=>"Queenstown YHA, 80 Lake Esplanade",
|
35
|
+
:id=>:YHQ},
|
36
|
+
{:name=>"Queenstown Airport", :description=>"Queenstown Airport", :id=>:ZQA},
|
37
|
+
{:name=>"Queenstown",
|
38
|
+
:description=>"Athol Street in the middle of the car park",
|
39
|
+
:id=>:ZQN},
|
40
|
+
{:name=>"Queenstown The Station",
|
41
|
+
:description=>"The Station, corner of Camp and Shotover Streets",
|
42
|
+
:id=>:ZQT}
|
43
|
+
]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
require 'dm-core'
|
4
|
+
require 'dm-aggregates'
|
5
|
+
require 'dm-migrations'
|
6
|
+
require 'dm-timestamps'
|
7
|
+
require 'dm-types'
|
8
|
+
require 'dm-validations'
|
9
|
+
|
10
|
+
module Luma
|
11
|
+
module Test
|
12
|
+
class TestDO
|
13
|
+
include DataMapper::Resource
|
14
|
+
|
15
|
+
property :id, String, :key => true, :length => 36
|
16
|
+
|
17
|
+
property :subject, String, :length => 30..155, :nullable => false
|
18
|
+
property :body, Text, :nullable => false
|
19
|
+
end # class Message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "Validation Responses" do
|
24
|
+
before(:all) do
|
25
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
26
|
+
DataMapper.auto_migrate!
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should generate a basic validation response" do
|
30
|
+
test_do = Luma::Test::TestDO.new
|
31
|
+
test_do.save.should be_false
|
32
|
+
|
33
|
+
ex = JSON.parse(JSClientBridge::Responses.respond_with_validation_error(test_do))
|
34
|
+
pp ex
|
35
|
+
ex['validation'].length.should == 3
|
36
|
+
ex['_short_type'].should == "TestDO"
|
37
|
+
ex['_type'].should == "Luma::Test::TestDO"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should generate a basic validation response using JSONP" do
|
41
|
+
test_do = Luma::Test::TestDO.new
|
42
|
+
test_do.save.should be_false
|
43
|
+
|
44
|
+
ex = JSClientBridge::Responses.respond_with_validation_error(test_do, :jsonp => 'jsonp1')
|
45
|
+
ex[0..6].should == 'jsonp1('
|
46
|
+
ex[-1..-1].should == ')'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
=begin
|
52
|
+
{
|
53
|
+
:_status => 'validation',
|
54
|
+
:_type => self.class,
|
55
|
+
:_short_type => self.class.to_s.split('::').first,
|
56
|
+
:_message => message,
|
57
|
+
:validation => self.errors.to_json
|
58
|
+
}
|
59
|
+
|
60
|
+
{
|
61
|
+
"validation"=> {
|
62
|
+
"body" => ["Body must not be blank"],
|
63
|
+
"subject" => ["Subject must not be blank", "Subject must be between 30 and 155 characters long"],
|
64
|
+
"id" => ["Id must not be blank"]
|
65
|
+
},
|
66
|
+
"_short_type" => "TestDO",
|
67
|
+
"_status" => "validation",
|
68
|
+
"_type" => "Luma::Test::TestDO",
|
69
|
+
"_message" => "Sorry, we couldn't save your TestDO"
|
70
|
+
}
|
71
|
+
|
72
|
+
=end
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: js-client-bridge
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Rolly
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-02 00:00:00 +13:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: extlib
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: rubyforge
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 2
|
53
|
+
- 0
|
54
|
+
- 4
|
55
|
+
version: 2.0.4
|
56
|
+
type: :development
|
57
|
+
version_requirements: *id003
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: gemcutter
|
60
|
+
prerelease: false
|
61
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
- 4
|
68
|
+
- 1
|
69
|
+
version: 0.4.1
|
70
|
+
type: :development
|
71
|
+
version_requirements: *id004
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: hoe
|
74
|
+
prerelease: false
|
75
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 2
|
81
|
+
- 5
|
82
|
+
- 0
|
83
|
+
version: 2.5.0
|
84
|
+
type: :development
|
85
|
+
version_requirements: *id005
|
86
|
+
description: Little library that encapsulates a (particular) standardised way of talking between a service and javascript. Probably not the best way of doing things, but it's been handy in a pinch.
|
87
|
+
email:
|
88
|
+
- rolly@luma.co.nz
|
89
|
+
executables: []
|
90
|
+
|
91
|
+
extensions: []
|
92
|
+
|
93
|
+
extra_rdoc_files:
|
94
|
+
- History.txt
|
95
|
+
- Manifest.txt
|
96
|
+
- README.txt
|
97
|
+
files:
|
98
|
+
- History.txt
|
99
|
+
- Manifest.txt
|
100
|
+
- README.txt
|
101
|
+
- Rakefile
|
102
|
+
- lib/js-client-bridge.rb
|
103
|
+
- lib/js-client-bridge/responses.rb
|
104
|
+
- lib/js-client-bridge/responses/ok.rb
|
105
|
+
- lib/js-client-bridge/responses/validation.rb
|
106
|
+
- lib/js-client-bridge/responses/error.rb
|
107
|
+
- lib/js-client-bridge/responses/exception.rb
|
108
|
+
- lib/js-client-bridge/extensions/datamapper_errors.rb
|
109
|
+
- script/console
|
110
|
+
- script/destroy
|
111
|
+
- script/generate
|
112
|
+
- spec/ok_responses_spec.rb
|
113
|
+
- spec/error_responses_spec.rb
|
114
|
+
- spec/exception_responses_spec.rb
|
115
|
+
- spec/validation_responses_spec.rb
|
116
|
+
- spec/spec.opts
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- tasks/rspec.rake
|
119
|
+
has_rdoc: true
|
120
|
+
homepage: https://github.com/luma/js-client-bridge/tree/master
|
121
|
+
licenses: []
|
122
|
+
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options:
|
125
|
+
- --main
|
126
|
+
- README.txt
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
segments:
|
134
|
+
- 0
|
135
|
+
version: "0"
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
version: "0"
|
143
|
+
requirements: []
|
144
|
+
|
145
|
+
rubyforge_project: js-client-bridge
|
146
|
+
rubygems_version: 1.3.6
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: Little library that encapsulates a (particular) standardised way of talking between a service and javascript
|
150
|
+
test_files: []
|
151
|
+
|