scrimp 1.0.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,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: []