simple-httpd 0.3.0 → 0.3.1

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/Gemfile +3 -1
  4. data/VERSION +1 -1
  5. data/bin/simple-httpd +1 -3
  6. data/examples/ex1.services/ex1_service_module.rb +8 -0
  7. data/examples/ex1/root.rb +4 -0
  8. data/examples/ex1/spec.rb +7 -0
  9. data/examples/ex2/info.rb +5 -3
  10. data/examples/ex3/example_service.rb +3 -2
  11. data/lib/simple/httpd.rb +37 -28
  12. data/lib/simple/httpd/base_controller.rb +6 -0
  13. data/lib/simple/httpd/base_controller/build_url.rb +1 -1
  14. data/lib/simple/httpd/base_controller/debug.rb +2 -0
  15. data/lib/simple/httpd/base_controller/error_handling.rb +21 -38
  16. data/lib/simple/httpd/base_controller/json.rb +2 -31
  17. data/lib/simple/httpd/base_controller/request.rb +23 -0
  18. data/lib/simple/httpd/base_controller/result.rb +19 -0
  19. data/lib/simple/httpd/cli.rb +71 -30
  20. data/lib/simple/httpd/helpers.rb +35 -3
  21. data/lib/simple/httpd/mount.rb +66 -0
  22. data/lib/simple/httpd/rack/dynamic_mount.rb +52 -32
  23. data/lib/simple/httpd/rack/static_mount.rb +20 -14
  24. data/lib/simple/httpd/route.rb +79 -0
  25. data/lib/simple/httpd/server.rb +28 -16
  26. data/lib/simple/httpd/service_adapter.rb +108 -0
  27. data/simple-httpd.gemspec +2 -1
  28. data/spec/simple/httpd/base_controller/httpd_cors_spec.rb +8 -3
  29. data/spec/simple/httpd/base_controller/httpd_url_building_spec.rb +17 -0
  30. data/spec/simple/httpd/helpers_spec.rb +25 -8
  31. data/spec/simple/httpd/loading_helpers_spec.rb +15 -0
  32. data/spec/simple/httpd/rspec_httpd_spec.rb +25 -9
  33. data/spec/simple/httpd/services/service_explicit_spec.rb +0 -5
  34. data/spec/simple/httpd/services/sideloading_spec.rb +9 -0
  35. data/spec/spec_helper.rb +2 -3
  36. metadata +31 -12
  37. data/examples/services/example_service.rb +0 -25
  38. data/lib/simple/httpd/mount_spec.rb +0 -106
  39. data/lib/simple/httpd/service.rb +0 -70
  40. data/lib/simple/service.rb +0 -69
  41. data/lib/simple/service/action.rb +0 -78
  42. data/lib/simple/service/context.rb +0 -46
  43. data/spec/simple/httpd/services/service_spec.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2cf16a40a84142155ccfdf2486e3f12d2a2be13429a942660136ba1c7ce5034
4
- data.tar.gz: fe679b913fe4ea849945b797fd949ec4d4f9dc783d7ea252e358060037ec25e9
3
+ metadata.gz: 4a7ef636de1fafcba61a0e8d7ed71a235d9c4700633f8ef8a899028415ffa1c7
4
+ data.tar.gz: 2018dc0e1bd99ec1bdc40524bfe2701be509e252cb6655b21e629e996da22614
5
5
  SHA512:
6
- metadata.gz: 36e023782c8602e0864b1787ee661b1b4c6df8b76ea28889a14acc77116b927c98dd7d720850337b011a7c32ad6694767a776ba92ed772953ebdf3960ff43932
7
- data.tar.gz: 0011d3b37a962c6dd5b1781f5dfd934aef319b8e13033b370fc9f4b240fa921ef06475808d7780072cd6c82675db95103d6c856911dd8a61d4048f8a45430bd3
6
+ metadata.gz: dd3fc097a2342e19b79c087fec69c92db23085c2352d323c3b284e60eaa764543048f731063e0d3c31ad3e136dc1bb2518456c00519f9231c0f3cde57a96f0d2
7
+ data.tar.gz: aff819af94b2b12d8e02c1c966dc9eeb9eb6d3b4a805778d609600d2dc9ff33dccb435983ecf4e1221e5346b899ccd7b358d625756fe26b912bf662b94770226
data/.rubocop.yml CHANGED
@@ -8,6 +8,7 @@ AllCops:
8
8
  - '*.gemspec'
9
9
  - 'Gemfile'
10
10
  - 'Rakefile'
11
+ - 'scripts/*.rb'
11
12
 
12
13
  Metrics/LineLength:
13
14
  Max: 140
@@ -87,3 +88,9 @@ Metrics/ParameterLists:
87
88
 
88
89
  Style/StringLiteralsInInterpolation:
89
90
  Enabled: false
91
+
92
+ Style/DoubleNegation:
93
+ Enabled: false
94
+
95
+ Style/ParallelAssignment:
96
+ Enabled: false
data/Gemfile CHANGED
@@ -6,12 +6,14 @@ gemspec
6
6
  # --- Local overrides for runtime dependencies ------------------------------
7
7
 
8
8
  # gem "simple-cli", path: "../simple-cli"
9
+ # gem "simple-service", path: "../simple-service"
9
10
 
10
11
  # --- Development and test dependencies ------------------------------
11
12
 
12
13
  group :development, :test do
13
- gem "rspec-httpd", "~> 0.3.0"
14
+ gem "rspec-httpd", "~> 0.3.2"
14
15
  # gem "rspec-httpd", path: "../rspec-httpd"
16
+ # # gem "simple-http", path: "../simple-http"
15
17
 
16
18
  gem 'rake', '~> 11'
17
19
  gem 'rspec', '~> 3.7'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.1
data/bin/simple-httpd CHANGED
@@ -8,6 +8,4 @@ Bundler.setup
8
8
  require "simple/cli"
9
9
  require "simple/httpd/cli"
10
10
 
11
- # run the :main command with these arguments. This switches simple-cli into
12
- # non-subcommand mode.
13
- Simple::Httpd::CLI.run!(:main)
11
+ Simple::CLI.run! Simple::Httpd::CLI
@@ -0,0 +1,8 @@
1
+ # Not a real service, but still loaded automatically when mounting ex1
2
+ module Ex1ServiceModule
3
+ extend self
4
+
5
+ def hello_world
6
+ "hello_world"
7
+ end
8
+ end
data/examples/ex1/root.rb CHANGED
@@ -9,3 +9,7 @@ end
9
9
  get "/exit" do
10
10
  exit 1
11
11
  end
12
+
13
+ get "/sideloaded_service" do
14
+ Ex1ServiceModule.hello_world
15
+ end
@@ -0,0 +1,7 @@
1
+ get "/full_url" do
2
+ full_url "foo", search: "s", page: 1
3
+ end
4
+
5
+ get "/url" do
6
+ url "foo", search: "s", page: 1
7
+ end
data/examples/ex2/info.rb CHANGED
@@ -1,4 +1,6 @@
1
- get "/inspect" do
2
- content_type :text
3
- request.env.map { |key, value| "#{key}=#{value}\n" }.grep(/^[A-Z]/).sort.join
1
+ [:get, :post, :put, :delete, :head].each do |verb|
2
+ send verb, "/inspect" do
3
+ content_type :text
4
+ request.env.map { |key, value| "#{key}=#{value}\n" }.grep(/^[A-Z]/).sort.join
5
+ end
4
6
  end
@@ -7,7 +7,8 @@ mount_service ExplicitService do |service|
7
7
  post "/echo/:a" => :explicit_echo
8
8
 
9
9
  put "/echo_context" do
10
- # def echo_context
11
- service.call(:echo_context, parsed_body, params, context: context)
10
+ ::Simple::Service.with_context(context) do
11
+ ::Simple::Service.invoke(service, :echo_context)
12
+ end
12
13
  end
13
14
  end
data/lib/simple/httpd.rb CHANGED
@@ -11,20 +11,29 @@ require "simple/service"
11
11
  require "simple/httpd/helpers"
12
12
  require "simple/httpd/base_controller"
13
13
  require "simple/httpd/version"
14
- require "simple/httpd/mount_spec"
14
+ require "simple/httpd/mount"
15
15
  require "simple/httpd/server"
16
16
 
17
- require "simple/httpd/service"
17
+ require "simple/httpd/service_adapter"
18
18
 
19
19
  class Simple::Httpd
20
20
  SELF = self
21
21
 
22
- def self.logger=(logger)
23
- @logger = logger
22
+ # returns a logger for Simple::Httpd.
23
+ #
24
+ # Initially we default to <tt>::Simple::CLI.logger</tt>. This gives colored
25
+ # logging during loading and mounting. Note that Simple::Httpd::Server builds
26
+ # its own logger instance to to pass that along to the web server.
27
+ def self.logger
28
+ @logger ||= ::Simple::CLI.logger
24
29
  end
25
30
 
26
- def self.logger
27
- @logger ||= ::Logger.new(STDERR, level: ::Logger::INFO)
31
+ def self.custom_logger?
32
+ @logger && @logger != ::Simple::CLI.logger
33
+ end
34
+
35
+ def self.logger=(logger)
36
+ @logger = logger
28
37
  end
29
38
 
30
39
  # Converts the passed in args into a Simple::Httpd application.
@@ -34,43 +43,49 @@ class Simple::Httpd
34
43
  # respond to call/3) it redirects to <tt>Server.listen!</tt> right
35
44
  # away - this way this method can be used as a helper method
36
45
  # to easily start a Rack server.
37
- def self.listen!(*mount_specs, environment: "development", host: nil, port:, logger: nil, &block)
46
+ def self.listen!(*mounts, environment: "development", host: nil, port:, &block)
38
47
  # If there is no argument but a block use the block as a rack server
39
48
  if block
40
- raise ArgumentError, "Can't deal w/block *and* mount_specs" unless mount_specs.empty?
49
+ raise ArgumentError, "Can't deal w/block *and* mounts" unless mounts.empty?
41
50
 
42
51
  app = block
43
- elsif mount_specs.length == 1 && mount_specs.first.respond_to?(:call)
52
+ elsif mounts.length == 1 && mounts.first.respond_to?(:call)
44
53
  # there is one argument, and that looks like a Rack app: return that.
45
- app = mount_specs.first
54
+ app = mounts.first
46
55
  else
47
56
  # Build a Httpd app, and listen
48
- app = build(*mount_specs)
57
+ app = build(*mounts)
49
58
  app.rack
50
59
  end
51
60
 
52
- Server.listen!(app, environment: environment, host: host, port: port, logger: logger)
61
+ Server.listen!(app, environment: environment, host: host, port: port)
53
62
  end
54
63
 
55
64
  # Converts the passed in arguments into a Simple::Httpd application.
56
65
  #
57
66
  # For a description of mounts see <tt>#add</tt>
58
- def self.build(*mount_specs)
59
- new(*mount_specs)
67
+ def self.build(*mounts)
68
+ new(*mounts)
60
69
  end
61
70
 
62
71
  private
63
72
 
64
73
  # Builds a Simple::Httpd application.
65
- def initialize(*mount_specs)
66
- @mount_specs = []
67
- mount_specs.map do |mount_spec|
68
- mount(mount_spec, at: nil)
74
+ def initialize(*mounts)
75
+ @mounts = []
76
+ mounts.map do |mount|
77
+ mount(mount, at: nil)
69
78
  end
70
79
  end
71
80
 
72
81
  public
73
82
 
83
+ def route_descriptions
84
+ @mounts.inject([]) do |ary, mount|
85
+ ary.concat mount.route_descriptions
86
+ end
87
+ end
88
+
74
89
  # Adds one or more mount_points
75
90
  #
76
91
  # Each entry in mounts can be either:
@@ -79,10 +94,10 @@ class Simple::Httpd
79
94
  # - a string denoting a mount_point, e.g. "path/to/root:/")
80
95
  # - a string denoting a "/" mount_point (e.g. "path", which is shorthand for "path:/")
81
96
  #
82
- def mount(mount_spec, at: nil)
97
+ def mount(mount, at: nil)
83
98
  raise ArgumentError, "Cannot mount onto an already built app" if built?
84
99
 
85
- @mount_specs << MountSpec.build(mount_spec, at: at)
100
+ @mounts << Mount.build(mount, at: at)
86
101
  end
87
102
 
88
103
  extend Forwardable
@@ -97,8 +112,8 @@ class Simple::Httpd
97
112
  def build_rack
98
113
  uri_map = {}
99
114
 
100
- @mount_specs.group_by(&:mount_point).map do |mount_point, mount_specs|
101
- apps = mount_specs.map(&:build_rack_apps).flatten
115
+ @mounts.group_by(&:mount_point).map do |mount_point, mounts|
116
+ apps = mounts.map(&:build_rack_apps).flatten
102
117
  uri_map[mount_point] = Rack.merge(apps)
103
118
  end
104
119
 
@@ -108,10 +123,4 @@ class Simple::Httpd
108
123
  def built?
109
124
  @rack != nil
110
125
  end
111
-
112
- public
113
-
114
- def listen!(environment:, port:, logger:)
115
- SELF.listen!(rack, environment: environment, port: port, logger: logger)
116
- end
117
126
  end
@@ -17,6 +17,12 @@ class Simple::Httpd::BaseController < Sinatra::Base
17
17
  end
18
18
  end
19
19
 
20
+ require_relative "./route"
21
+
22
+ class Simple::Httpd::BaseController
23
+ extend ::Simple::Httpd::RouteDescriptions
24
+ end
25
+
20
26
  Dir.chdir __dir__ do
21
27
  Dir.glob("base_controller/*.rb").sort.each do |file|
22
28
  require_relative file
@@ -28,7 +28,7 @@ class Simple::Httpd::BaseController
28
28
  private
29
29
 
30
30
  def build_url_query(params)
31
- params = params.reject { |_k, v| v.nil? || v.empty? }
31
+ params = params.reject { |_k, v| v.nil? || v.to_s.empty? }
32
32
  return nil if params.empty?
33
33
 
34
34
  params.map { |k, value| "#{k}=#{escape(value.to_s)}" }.join("&")
@@ -1,6 +1,8 @@
1
1
  class Simple::Httpd::BaseController
2
2
  helpers do
3
3
  def debug(data)
4
+ require "pp"
5
+
4
6
  content_type "text/plain"
5
7
  halt data.pretty_inspect
6
8
  end
@@ -1,7 +1,5 @@
1
1
  require_relative "./json"
2
2
 
3
- # rubocop:disable Metrics/ClassLength
4
-
5
3
  class Simple::Httpd::BaseController
6
4
  H = ::Simple::Httpd::Helpers
7
5
 
@@ -9,8 +7,6 @@ class Simple::Httpd::BaseController
9
7
  set :dump_errors, false
10
8
  set :raise_errors, false
11
9
 
12
- set :raise_errors, true if ENV["RACK_ENV"] == "test"
13
-
14
10
  private
15
11
 
16
12
  def stringify_hash(hsh)
@@ -127,19 +123,27 @@ class Simple::Httpd::BaseController
127
123
 
128
124
  # -- print unspecified errors.
129
125
 
130
- error do |exc|
131
- content_type :text
132
- message = <<~MSG
133
- === #{exc.class.name} =====================
134
- #{exc.message.chomp}
135
-
136
- #{filtered_backtrace(exc)}
137
- ==================================================================
138
- MSG
139
-
140
- STDERR.puts message
141
- status 500
142
- "\n#{message}\n"
126
+ if ENV["RACK_ENV"] == "production"
127
+ error do |exc|
128
+ content_type :text
129
+ status 500
130
+ exc.class.name
131
+ end
132
+ else
133
+ error do |exc|
134
+ content_type :text
135
+ message = <<~MSG
136
+ === #{exc.class.name} =====================
137
+ #{exc.message.chomp}
138
+
139
+ #{H.filtered_stacktrace(exc.backtrace).join("\n")}
140
+ ==================================================================
141
+ MSG
142
+
143
+ STDERR.puts message
144
+ status 500
145
+ "\n#{message}\n"
146
+ end
143
147
  end
144
148
 
145
149
  private
@@ -152,25 +156,4 @@ class Simple::Httpd::BaseController
152
156
  exc.message
153
157
  # "#{exc.message}, from #{exc.backtrace.join("\n\t")}"
154
158
  end
155
-
156
- def remove_wd(str)
157
- @wd ||= Dir.getwd
158
-
159
- if str.start_with?(@wd)
160
- str[(@wd.length + 1)..-1]
161
- else
162
- str
163
- end
164
- end
165
-
166
- def filtered_backtrace(exc, count: 20)
167
- lines = exc.backtrace.map do |line|
168
- next "...\n" if line =~ /\.rvm\b/
169
-
170
- "#{remove_wd(line)}\n"
171
- end
172
-
173
- s = lines[0, count].join("")
174
- s.gsub(/(\.\.\.\n)+/, " ... (lines removed) ...\n")
175
- end
176
159
  end
@@ -12,42 +12,13 @@ class Simple::Httpd::BaseController
12
12
 
13
13
  configure :development, :test do
14
14
  begin
15
- @@neatjson = true
16
15
  require "neatjson"
17
16
  rescue LoadError
18
- @@neatjson = false
17
+ :nop
19
18
  end
20
19
 
21
20
  def generate_json(result)
22
- if @@neatjson
23
- JSON.neat_generate(result)
24
- else
25
- JSON.pretty_generate(result)
26
- end
27
- end
28
- end
29
-
30
- public
31
-
32
- def parsed_body
33
- return @parsed_body if defined? @parsed_body
34
-
35
- @parsed_body = parse_body
36
- rescue RuntimeError => e
37
- raise ArgumentError, e.to_s
38
- end
39
-
40
- private
41
-
42
- def parse_body
43
- case request.media_type
44
- when "application/json"
45
- request.body.rewind
46
- body = request.body.read
47
- body == "" ? {} : JSON.parse(body)
48
- else
49
- # parses form data
50
- request.POST
21
+ JSON.respond_to?(:neat_generate) ? JSON.neat_generate(result) : JSON.pretty_generate(result)
51
22
  end
52
23
  end
53
24
  end
@@ -0,0 +1,23 @@
1
+ class Simple::Httpd::BaseController
2
+ def parsed_body
3
+ return @parsed_body if defined? @parsed_body
4
+
5
+ @parsed_body = parse_body
6
+ rescue RuntimeError => e
7
+ raise ArgumentError, e.to_s
8
+ end
9
+
10
+ private
11
+
12
+ def parse_body
13
+ case request.media_type
14
+ when "application/json"
15
+ request.body.rewind
16
+ body = request.body.read
17
+ body == "" ? {} : JSON.parse(body)
18
+ else
19
+ # parses form data
20
+ request.POST
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ class ::Simple::Httpd::BaseController
2
+ private
3
+
4
+ # encodes the result, according to its payload.
5
+ #
6
+ # This function is used by the service integration code, but
7
+ # is potentially useful outside.
8
+ def encode_result(result)
9
+ case result
10
+ when Array, Hash
11
+ json(result)
12
+ when String
13
+ content_type :text
14
+ result
15
+ else
16
+ result
17
+ end
18
+ end
19
+ end