scrimp 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ /* http://meyerweb.com/eric/tools/css/reset/
2
+ v2.0 | 20110126
3
+ License: none (public domain)
4
+ */
5
+
6
+ html, body, div, span, applet, object, iframe,
7
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8
+ a, abbr, acronym, address, big, cite, code,
9
+ del, dfn, em, img, ins, kbd, q, s, samp,
10
+ small, strike, strong, sub, sup, tt, var,
11
+ b, u, i, center,
12
+ dl, dt, dd, ol, ul, li,
13
+ fieldset, form, label, legend,
14
+ table, caption, tbody, tfoot, thead, tr, th, td,
15
+ article, aside, canvas, details, embed,
16
+ figure, figcaption, footer, header, hgroup,
17
+ menu, nav, output, ruby, section, summary,
18
+ time, mark, audio, video {
19
+ margin: 0;
20
+ padding: 0;
21
+ border: 0;
22
+ font-size: 100%;
23
+ font: inherit;
24
+ vertical-align: baseline;
25
+ }
26
+ /* HTML5 display-role reset for older browsers */
27
+ article, aside, details, figcaption, figure,
28
+ footer, header, hgroup, menu, nav, section {
29
+ display: block;
30
+ }
31
+ body {
32
+ line-height: 1;
33
+ }
34
+ ol, ul {
35
+ list-style: none;
36
+ }
37
+ blockquote, q {
38
+ quotes: none;
39
+ }
40
+ blockquote:before, blockquote:after,
41
+ q:before, q:after {
42
+ content: '';
43
+ content: none;
44
+ }
45
+ table {
46
+ border-collapse: collapse;
47
+ border-spacing: 0;
48
+ }
@@ -0,0 +1,171 @@
1
+ # coding: UTF-8
2
+
3
+ module Scrimp
4
+ module ThriftUtil
5
+ class << self
6
+ # use a fully-qualified name to get a constant
7
+ # no, it really doesn't belong here
8
+ # but life will almost certainly go on
9
+ def qualified_const(name)
10
+ name.split('::').inject(Object) {|obj, name| obj.const_get(name)}
11
+ end
12
+
13
+ def application_exception_type_string(type)
14
+ case type
15
+ when Thrift::ApplicationException::UNKNOWN
16
+ 'UNKNOWN'
17
+ when Thrift::ApplicationException::UNKNOWN_METHOD
18
+ 'UNKNOWN_METHOD'
19
+ when Thrift::ApplicationException::INVALID_MESSAGE_TYPE
20
+ 'INVALID_MESSAGE_TYPE'
21
+ when Thrift::ApplicationException::WRONG_METHOD_NAME
22
+ 'WRONG_METHOD_NAME'
23
+ when Thrift::ApplicationException::BAD_SEQUENCE_ID
24
+ 'BAD_SEQUENCE_ID'
25
+ when Thrift::ApplicationException::MISSING_RESULT
26
+ 'MISSING_RESULT'
27
+ when Thrift::ApplicationException::INTERNAL_ERROR
28
+ 'INTERNAL_ERROR'
29
+ when Thrift::ApplicationException::PROTOCOL_ERROR
30
+ 'PROTOCOL_ERROR'
31
+ else
32
+ type
33
+ end
34
+ end
35
+
36
+ # For every Thrift service, the code generator produces a module. This
37
+ # returns a list of all such modules currently loaded.
38
+ def service_modules
39
+ modules = []
40
+ ObjectSpace.each_object(Module) do |clazz|
41
+ if clazz < Thrift::Client
42
+ modules << qualified_const(clazz.name.split('::')[0..-2].join('::')) # i miss activesupport...
43
+ end
44
+ end
45
+ modules.delete(Thrift)
46
+ modules
47
+ end
48
+
49
+ # Given a service module (see service_modules above), returns a list of the names of all
50
+ # the functions of that Thrift service.
51
+ def service_functions(service_module)
52
+ methods = service_module.const_get('Client').instance_methods.map {|method| method.to_s}
53
+ methods.select do |method|
54
+ send_exists = methods.include? "send_#{method}"
55
+ upped = method.dup
56
+ upped[0..0] = method[0..0].upcase
57
+ begin
58
+ send_exists && service_module.const_get("#{upped}_args")
59
+ rescue
60
+ false
61
+ end
62
+ end
63
+ end
64
+
65
+ # Given a service module (see service_modules above) and a Thrift function name,
66
+ # returns the class for the structure representing the function's arguments.
67
+ def service_args(service_module, function_name)
68
+ function_name = function_name.dup
69
+ function_name[0..0] = function_name[0..0].upcase
70
+ service_module.const_get("#{function_name}_args")
71
+ end
72
+
73
+ # Given a service module (see service_modules above) and a Thrift function name,
74
+ # returns the class for the structure representing the function's return value.
75
+ def service_result(service_module, function_name)
76
+ function_name = function_name.dup
77
+ function_name[0..0] = function_name[0..0].upcase
78
+ service_module.const_get("#{function_name}_result")
79
+ end
80
+
81
+ # Returns a list of the classes for all Thrift structures that were loaded
82
+ # at the time extend_structs was called (see below).
83
+ def all_structs
84
+ @@all_structs
85
+ end
86
+
87
+ # Finds all loaded Thrift struct classes, adds methods to them
88
+ # for building them from hashes, and saves the list of them for
89
+ # future reference.
90
+ def extend_structs
91
+ @@all_structs = []
92
+ ObjectSpace.each_object(Module) do |clazz|
93
+ if Thrift::Struct > clazz || Thrift::Union > clazz
94
+ clazz.extend(JsonThrift)
95
+ @@all_structs << clazz
96
+ end
97
+ end
98
+ end
99
+
100
+ # Converts a Thrift struct to a hash (suitable for conversion to json).
101
+ def thrift_struct_to_json_map(value, clazz)
102
+ result = {}
103
+ clazz.const_get('FIELDS').each do |(_, struct_field)|
104
+ name = struct_field[:name]
105
+ val = value.send(name)
106
+ result[name] = thrift_type_to_json_type(val, struct_field) if val
107
+ end
108
+ result
109
+ end
110
+
111
+ # Converts a Thrift union to a hash (suitable for conversion to json).
112
+ def thrift_union_to_json_map(value, clazz)
113
+ result = {}
114
+ name = value.get_set_field.to_s
115
+ struct_field = clazz.const_get('FIELDS').find{|x| x[1][:name] == name}[1]
116
+ result[name] = thrift_type_to_json_type(value.get_value, struct_field)
117
+ result
118
+ end
119
+
120
+ # Converts a Thrift value to a primitive, list, or hash (suitable for conversion to json).
121
+ # The value is interpreted using a type info hash of the format returned by #type_info.
122
+ def thrift_type_to_json_type(value, field)
123
+ type = Thrift.type_name(field[:type])
124
+ raise Thrift::TypeError.new("Type for #{field.inspect} not found.") unless type
125
+ type.sub!('Types::', '')
126
+ result = value
127
+ type = 'UNION' if type == 'STRUCT' && field[:class].ancestors.any?{|x| x == Thrift::Union}
128
+ if type == 'STRUCT'
129
+ result = thrift_struct_to_json_map(value, field[:class])
130
+ # field[:class].const_get('FIELDS').each do |(_, struct_field)|
131
+ # name = struct_field[:name]
132
+ # val = value.send(name)
133
+ # result[name] = thrift_type_to_json_type(val, struct_field) if val
134
+ # end
135
+ elsif type == 'LIST' || type == 'SET'
136
+ result = value.map {|val| thrift_type_to_json_type val, field[:element]}
137
+ elsif type == 'MAP'
138
+ result = value.map do |key, val|
139
+ [thrift_type_to_json_type(key, field[:key]), thrift_type_to_json_type(val, field[:value])]
140
+ end
141
+ elsif enum = field[:enum_class]
142
+ result = enum.const_get('VALUE_MAP')[value] || value
143
+ elsif type == 'UNION'
144
+ result = thrift_union_to_json_map(value, field[:class])
145
+ end
146
+ result
147
+ end
148
+
149
+ # Given a field description (as found in the FIELDS constant of a Thrift struct class),
150
+ # returns a hash containing these elements:
151
+ # - type - the name of the type, such as UNION, STRUCT, etc
152
+ # - key (for maps) - the type info hash of the map's keys
153
+ # - value (for maps) - the type info hash of the map's values
154
+ # - element (for lists, sets) - the type info hash of the collection's elements
155
+ # - enum (for enums) - a map of enum numeric value to name for the enum values
156
+ def type_info(field)
157
+ field = field.dup
158
+ field[:type] = Thrift.type_name(field[:type]).sub('Types::', '')
159
+ field.delete :name
160
+ field[:key] = type_info(field[:key]) if field[:key]
161
+ field[:value] = type_info(field[:value]) if field[:value]
162
+ field[:element] = type_info(field[:element]) if field[:element]
163
+ if enum = field[:enum_class]
164
+ field[:enum] = enum.const_get 'VALUE_MAP'
165
+ end
166
+ field
167
+ end
168
+ end
169
+ end
170
+ end
171
+
@@ -0,0 +1,5 @@
1
+ # coding: UTF-8
2
+
3
+ module Scrimp
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,45 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title scrimp
5
+ %script{:type => 'text/javascript', :src => '/jquery-2.0.3.min.js'}
6
+ %script{:type => 'text/coffeescript', :src => '/main.coffee'}
7
+ %script{:type => 'text/javascript', :src => '/coffee-script-1.6.3.min.js'}
8
+ %link{:rel => 'stylesheet/css', :type => 'text/css', :href => '/reset.css'}
9
+ %link{:rel => 'stylesheet/less', :type => 'text/css', :href => '/main.less'}
10
+ %script{:type => 'text/javascript', :src => '/less-1.5.0.min.js'}
11
+ %body
12
+ .content.split-vertical
13
+ .request
14
+ %form.request-form{:action => "/invoke", :method => "post"}
15
+ %input.invoke{:type => 'submit', :value => 'Invoke'}
16
+ .structured-request
17
+ %a.show-raw-request{:href => '#'} edit request as json
18
+ .field
19
+ %label.service-field{:name => 'service'} Service
20
+ %select.service-field{:name => 'service'}
21
+ .field
22
+ %label.function-field{:name => 'function'} Function
23
+ %select.function-field{:name => 'function'}
24
+ .field
25
+ %label.protocol-field{:name => 'protocol'} Protocol
26
+ %select.protocol-field{:name => 'protocol'}
27
+ .field
28
+ %label.host-field{:name => 'host'} Host
29
+ %input.host-field{:name => 'host', :type => 'text'}
30
+ .field
31
+ %label.port-field{:name => 'port'} Port
32
+ %input.port-field{:name => 'port', :type => 'text'}
33
+ %ul.args-field.struct-field
34
+ .raw-request
35
+ %a.show-structured-request{:href => '#'} use structured editor
36
+ %textarea.request-json{:name => 'request-json'}
37
+ .response
38
+ .structured-response
39
+ %a.show-raw-response{:href => '#'} view json
40
+ %p.response-success Success!
41
+ %p.response-error
42
+ .response-details
43
+ .raw-response
44
+ %a.show-structured-response{:href => '#'} view formatted
45
+ .response-json
@@ -0,0 +1,32 @@
1
+ struct WordStats {
2
+ 1: i64 count
3
+ 2: double percentage
4
+ 3: bool palindrome
5
+ }
6
+
7
+ enum Word {
8
+ BRUMAL = 1
9
+ CYCLOPLEAN = 2
10
+ FANTOD = 3
11
+ }
12
+
13
+ struct Person {
14
+ 1: string name
15
+ 2: optional Word favoriteWord
16
+ }
17
+
18
+ exception Tantrum {
19
+ 1: string complaint
20
+ }
21
+
22
+ service ExampleService {
23
+ map<string, WordStats> textStats(1: string text)
24
+
25
+ string greet(1: set<Person> people)
26
+
27
+ double random()
28
+
29
+ void voidMethod(1: bool throwException) throws (1: Tantrum tantrum)
30
+
31
+ oneway void onewayMethod(1: string message)
32
+ }
data/sample/server.rb ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'tmpdir'
5
+
6
+ options = {port: 9000,
7
+ thrift_command: 'thrift'}
8
+ parser = OptionParser.new do |op|
9
+ op.on '-p', '--port PORT', 'port to launch Thrift server on' do |port|
10
+ options[:port] = port.to_i
11
+ end
12
+ op.on '-t', '--thrift-command COMMAND', 'thrift compiler' do |cmd|
13
+ options[:thrift_command] = cmd
14
+ end
15
+ op.on '-h', '--help' do
16
+ puts parser
17
+ exit 0
18
+ end
19
+ end
20
+
21
+ begin
22
+ parser.parse!
23
+ rescue
24
+ puts parser
25
+ exit 0
26
+ end
27
+
28
+ Dir.mktmpdir do |out|
29
+ path = File.expand_path(File.join(File.dirname(__FILE__), 'example.thrift'))
30
+ puts (cmd = "#{options[:thrift_command]} --gen rb --out #{out} #{path}")
31
+ puts `#{cmd}`
32
+ $LOAD_PATH.unshift out
33
+ Dir["#{out}/*.rb"].each {|file| require file}
34
+ $LOAD_PATH.delete out
35
+ end
36
+
37
+ class ExampleServiceImpl
38
+ include ExampleService
39
+
40
+ # Example with map and struct in response.
41
+ def textStats(text)
42
+ words = text.split(/\b/).map {|w| w.gsub(/\W/, '').downcase}.reject(&:empty?)
43
+ results = {}
44
+ words.uniq.each do |word|
45
+ results[word] = WordStats.new count: words.count(word),
46
+ percentage: words.count(word).to_f / words.count.to_f,
47
+ palindrome: word == word.reverse
48
+ end
49
+ results
50
+ end
51
+
52
+ # Example with set, struct, and optional field in request.
53
+ GREETINGS = {1 => "Stay warm!",
54
+ 2 => "Watch out for eldritch horrors!",
55
+ 3 => "Try to calm down."}
56
+ def greet(people)
57
+ str = ""
58
+ people.each do |person|
59
+ str << "Hello, #{person.name}! #{GREETINGS[person.favoriteWord]}\n"
60
+ end
61
+ str
62
+ end
63
+
64
+ # Example with no request params.
65
+ def random
66
+ rand
67
+ end
68
+
69
+ def voidMethod(throwException)
70
+ raise Tantrum.new("We're out of hot chocolate!") if throwException
71
+ end
72
+
73
+ def onewayMethod(message)
74
+ puts "I received the following message, which I fully intend to ignore: #{message}"
75
+ end
76
+ end
77
+
78
+ processor = ExampleServiceImpl::Processor.new(ExampleServiceImpl.new)
79
+ transport = Thrift::ServerSocket.new('localhost', options[:port])
80
+ transport_factory = Thrift::FramedTransportFactory.new
81
+ protocol_factory = Thrift::CompactProtocolFactory.new
82
+ server = Thrift::SimpleServer.new(processor, transport, transport_factory, protocol_factory)
83
+
84
+ puts "Starting example service for localhost on port #{options[:port]}"
85
+ server.serve
data/scrimp.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/scrimp/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jacob Williams"]
6
+ gem.email = ["jacob.williams@cerner.com"]
7
+ gem.description = %q{Web UI for making requests to thrift services, given their IDL files.}
8
+ gem.summary = %q{Generic UI for thrift services.}
9
+ gem.homepage = "https://github.com/cerner/scrimp"
10
+ gem.licenses = ['Apache 2']
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "scrimp"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Scrimp::VERSION
18
+
19
+ gem.add_runtime_dependency 'thrift', '~> 0.9.1'
20
+ gem.add_runtime_dependency 'thin', '~> 1.6.0' # https://issues.apache.org/jira/browse/THRIFT-2145
21
+ gem.add_runtime_dependency 'haml', '~> 4.0.3'
22
+ gem.add_runtime_dependency 'json', '~> 1.8.1'
23
+ gem.add_runtime_dependency 'sinatra', '~> 1.4.4'
24
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scrimp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jacob Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thrift
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: thin
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: haml
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 4.0.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 4.0.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: json
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.8.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.8.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 1.4.4
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 1.4.4
83
+ description: Web UI for making requests to thrift services, given their IDL files.
84
+ email:
85
+ - jacob.williams@cerner.com
86
+ executables:
87
+ - scrimp
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - CONTRIBUTORS.txt
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - NOTICE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/scrimp
99
+ - lib/scrimp.rb
100
+ - lib/scrimp/app.rb
101
+ - lib/scrimp/json_thrift.rb
102
+ - lib/scrimp/public/coffee-script-1.6.3.min.js
103
+ - lib/scrimp/public/jquery-2.0.3.min.js
104
+ - lib/scrimp/public/less-1.5.0.min.js
105
+ - lib/scrimp/public/main.coffee
106
+ - lib/scrimp/public/main.less
107
+ - lib/scrimp/public/reset.css
108
+ - lib/scrimp/thrift_util.rb
109
+ - lib/scrimp/version.rb
110
+ - lib/scrimp/views/index.haml
111
+ - sample/example.thrift
112
+ - sample/server.rb
113
+ - scrimp.gemspec
114
+ homepage: https://github.com/cerner/scrimp
115
+ licenses:
116
+ - Apache 2
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.0.3
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Generic UI for thrift services.
138
+ test_files: []