hobby 0.0.12 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d2d480ccdf4a60839072589bfaa58309a00bf4a
4
- data.tar.gz: e69b2439193e69c2f53adc780dac6666f71a01e6
3
+ metadata.gz: 1c820bc2f67ffcbd87d4de89a2e8ad53a6f83da9
4
+ data.tar.gz: e56880e61b2d283537bf44bd019709440dde9118
5
5
  SHA512:
6
- metadata.gz: ff31f1f11344235dec85b8d021008a1b89a18ab65b8e4743add83b8e8e8e3104af567c8bdb01a5e84d6a78c5c9248a95272e3de7f3ac88fc6c60180a3e7266ee
7
- data.tar.gz: 723ba318c713c9d21c44c3bf8397e48a7204a562c7294545aedd7a26634a402887269878618cc61e91b3a2dcbe3454a0aa590dfde07f1cc51532c0d8123a0f2f
6
+ metadata.gz: f14f9c47364424a1826184cd11a670eed4509e315bb83b6f978e05022fd17da57d3fc139d1e40c62ea33c06ebf59c38996f4dfb90ea0bef779b2a67b837321b5
7
+ data.tar.gz: 27b2ac428b2a8a7b51941a4e4dd7bab6d9c28e1cbbb4e77d49c282ce58638e0500913b6320f2090511a4a5ca7793b9ba1afd9099748ba17b67afde31214b3142
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'devtools', github: 'mbj/devtools'
4
- gem 'mutant', github: 'mbj/mutant'
3
+ gem 'devtools'
4
+ gem 'mutant'
5
5
  gem 'minitest'
6
6
  gem 'minitest-power_assert'
7
7
  gem 'rack-test'
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'hobby'
7
- spec.version = '0.0.12'
7
+ spec.version = '0.1.0'
8
8
  spec.authors = ['Anatoly Chernow']
9
9
  spec.email = ['chertoly@gmail.com']
10
10
  spec.summary = %q{A minimal DSL over rack}
@@ -10,20 +10,19 @@ module Hobby
10
10
 
11
11
  def self.included app
12
12
  app.extend Singleton
13
- app.builder, app.router = Rack::Builder.new, Router.new
13
+ app.router = Router.new
14
14
  app.request, app.response = Rack::Request, Rack::Response
15
15
  end
16
16
 
17
17
  module Singleton
18
- attr_accessor :builder, :router, :request, :response
18
+ attr_accessor :router, :request, :response
19
19
 
20
20
  def new (*)
21
- builder.run super
22
- builder.to_app
21
+ super.router.to_rack_app
23
22
  end
24
23
 
25
24
  extend Forwardable
26
- delegate [:map, :use] => :builder
25
+ delegate [:map, :use] => :router
27
26
 
28
27
  VERBS.each do |verb|
29
28
  define_method verb.downcase do |path = '/', &action|
@@ -41,7 +40,7 @@ module Hobby
41
40
  protected
42
41
  def handle env
43
42
  catch :halt do
44
- @route = self.class.router.route_for (@env = env)
43
+ @route = router.route_for (@env = env)
45
44
  fill_body
46
45
  response
47
46
  end
@@ -1,7 +1,19 @@
1
1
  module Hobby
2
2
  module Helpers
3
+ extend Forwardable
4
+
3
5
  attr_reader :env, :route
4
6
 
7
+ def router
8
+ @router ||= begin
9
+ router = self.class.router.clone
10
+ router.app = self
11
+ router
12
+ end
13
+ end
14
+
15
+ delegate [:map, :use] => :router
16
+
5
17
  def request
6
18
  @request ||= self.class.request.new env
7
19
  end
@@ -2,6 +2,12 @@ module Hobby
2
2
  class Router
3
3
  def initialize
4
4
  @routes = Routes.new
5
+ @uses, @maps = [], []
6
+ end
7
+
8
+ def initialize_copy _router
9
+ @uses = instance_variable_get(:@uses).dup
10
+ @maps = instance_variable_get(:@maps).dup
5
11
  end
6
12
 
7
13
  def add_route verb, path, &action
@@ -17,8 +23,33 @@ module Hobby
17
23
  route, params = @routes["#{env['REQUEST_METHOD']}#{env['PATH_INFO']}"]
18
24
  params ? route.with_params(params) : route
19
25
  end
26
+
27
+ def use *all
28
+ @uses << all
29
+ end
30
+
31
+ def map path, app = nil, &block
32
+ @maps << Builder::Map.new(path, app, &block)
33
+ end
34
+
35
+ attr_accessor :app
36
+
37
+ def to_rack_app
38
+ builder = create_builder
39
+ builder.run app
40
+ builder.to_app
41
+ end
42
+
43
+ private
44
+ def create_builder
45
+ builder = Builder.new
46
+ @uses.each { |all| builder.add_use *all }
47
+ @maps.each { |map| builder.add_map map }
48
+ builder
49
+ end
20
50
  end
21
51
  end
22
52
 
53
+ require 'hobby/router/builder'
23
54
  require 'hobby/router/routes'
24
55
  require 'hobby/router/route'
@@ -0,0 +1,25 @@
1
+ module Hobby
2
+ class Router
3
+ class Builder < Rack::Builder
4
+ # To work around
5
+ # https://github.com/mbj/mutant#the-crash--stuck-problem-mri
6
+ alias add_use use
7
+ alias add_map2 map
8
+
9
+ def add_map map
10
+ if map.app
11
+ add_map2 map.path do run map.app end
12
+ else
13
+ add_map2 map.path, &map.block
14
+ end
15
+ end
16
+
17
+ class Map
18
+ attr_reader :path, :app, :block
19
+ def initialize path, app, &block
20
+ @path, @app, @block = path, app, block
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -285,8 +285,8 @@ to '/anatoly' and '/patricio' routes:
285
285
  class App
286
286
  include Hobby
287
287
 
288
- map('/anatoly') { run C.new 'Anatoly' }
289
- map('/patricio') { run C.new 'Patricio' }
288
+ map '/anatoly', C.new('Anatoly')
289
+ map '/patricio', C.new('Patricio')
290
290
 
291
291
  get '/' do
292
292
  'Mapping app.'
@@ -327,7 +327,6 @@ Many components of an application can be customized or replaced.
327
327
  class App
328
328
  include Hobby
329
329
 
330
- self.builder = custom_builder
331
330
  self.router = custom_router
332
331
  self.request = custom_request
333
332
  self.response = custom_response
@@ -75,6 +75,17 @@ describe Hobby::App do
75
75
  end
76
76
  end
77
77
 
78
+ describe '#router' do
79
+ it 'gives a distinct router to each app' do
80
+ app = Class.new { include Hobby }
81
+
82
+ first_instance = app.new
83
+ second_instance = app.new
84
+
85
+ assert { not first_instance.router.equal? second_instance.router }
86
+ end
87
+ end
88
+
78
89
  describe :integration do
79
90
  before do
80
91
  described_class.app = build_app described_class
@@ -89,6 +100,51 @@ describe Hobby::App do
89
100
  get '/map'
90
101
  assert { last_response.body == 'from map' }
91
102
  end
103
+
104
+ it 'mounts an application to the rack stack with old deprecated syntax' do
105
+ get '/deprecated_map'
106
+ assert { last_response.body == 'from deprecated map' }
107
+ end
108
+ end
109
+
110
+ describe MapInsideInitialize do
111
+ describe 'without any passed arguments' do
112
+ def app
113
+ described_class.app.new
114
+ end
115
+
116
+ it do
117
+ get '/'
118
+ assert { last_response.body == 'hello world' }
119
+ get '/first_map'
120
+ assert { last_response.body == 'first mapapp' }
121
+ get '/second_map'
122
+ assert { last_response.body == 'second mapapp' }
123
+ end
124
+ end
125
+
126
+ describe 'with passed routes' do
127
+ def app
128
+ some_app = Class.new {
129
+ include Hobby
130
+ get { 'Some string.' }
131
+ }
132
+ routes = { '/third_map' => some_app.new }
133
+ described_class.app.new routes
134
+ end
135
+
136
+ it do
137
+ get '/'
138
+ assert { last_response.body == 'hello world' }
139
+ get '/first_map'
140
+ assert { last_response.body == 'first mapapp' }
141
+ get '/second_map'
142
+ assert { last_response.body == 'second mapapp' }
143
+
144
+ get '/third_map'
145
+ assert { last_response.body == 'Some string.' }
146
+ end
147
+ end
92
148
  end
93
149
 
94
150
  describe Use do
@@ -98,6 +154,13 @@ describe Hobby::App do
98
154
  end
99
155
  end
100
156
 
157
+ describe UseInsideInitialize do
158
+ it do
159
+ get '/'
160
+ assert { last_response.content_type == 'application/json' }
161
+ end
162
+ end
163
+
101
164
  describe WithoutPath do
102
165
  it 'is accessible as /' do
103
166
  get '/'
@@ -171,5 +234,43 @@ describe Hobby::App do
171
234
  assert { last_response.body == 'Created.' }
172
235
  end
173
236
  end
237
+
238
+ describe UnshareableRouterMaps do
239
+ it do
240
+ get '/1/first'
241
+ assert { last_response.body == 'The name is A.' }
242
+ get '/1/second'
243
+ assert { last_response.body == 'The name is B.' }
244
+ get '/1/third'
245
+ assert { last_response.body == '404' }
246
+
247
+ get '/2/first'
248
+ assert { last_response.body == 'The name is A.' }
249
+ get '/2/second'
250
+ assert { last_response.body == 'The name is B.' }
251
+ get '/2/third'
252
+ assert { last_response.body == 'The name is C.' }
253
+
254
+ get '/3/first'
255
+ assert { last_response.body == 'The name is A.' }
256
+ get '/3/second'
257
+ assert { last_response.body == 'The name is B.' }
258
+ get '/3/third'
259
+ assert { last_response.body == '404' }
260
+ end
261
+ end
262
+
263
+ describe UnshareableRouterUses do
264
+ it do
265
+ get '/1'
266
+ assert { last_response.content_type == 'application/html' }
267
+
268
+ get '/2'
269
+ assert { last_response.content_type == 'application/json' }
270
+
271
+ get '/3'
272
+ assert { last_response.content_type == 'application/html' }
273
+ end
274
+ end
174
275
  end
175
276
  end
@@ -1,5 +1,5 @@
1
- map '/map' do
2
- run Proc.new { |env| [200, {}, ['from map']] }
1
+ map '/map', Proc.new { |env| [200, {}, ['from map']] }
2
+ map '/deprecated_map' do
3
+ run Proc.new { |env| [200, {}, ['from deprecated map']] }
3
4
  end
4
-
5
5
  get('/') { 'hello world' }
@@ -0,0 +1,13 @@
1
+ first_app = Proc.new { |env| [200, {}, ['first mapapp']] }
2
+ second_app = Proc.new { |env| [200, {}, ['second mapapp']] }
3
+
4
+ map '/first_map', first_app
5
+ map '/second_map', second_app
6
+
7
+ get('/') { 'hello world' }
8
+
9
+ def initialize hash = {}
10
+ hash.each do |route, app|
11
+ map route, app
12
+ end
13
+ end
@@ -1,17 +1,15 @@
1
- map('/nested') do
2
- nested_app = Class.new do
3
- include Hobby::App
1
+ nested_app = Class.new do
2
+ include Hobby::App
4
3
 
5
- def initialize first, second
6
- @a = first
7
- @b = second
8
- @c = yield
9
- end
10
-
11
- get do
12
- "#{@a}:#{@b}:#{@c}"
13
- end
4
+ def initialize first, second
5
+ @a = first
6
+ @b = second
7
+ @c = yield
14
8
  end
15
9
 
16
- run nested_app.new(:a, :b) { :c }
10
+ get do
11
+ "#{@a}:#{@b}:#{@c}"
12
+ end
17
13
  end
14
+
15
+ map '/nested', nested_app.new(:a, :b) { :c }
@@ -5,4 +5,12 @@ self.router = Class.new {
5
5
  def route_for _request
6
6
  -> { 'for any route' }
7
7
  end
8
+
9
+ attr_accessor :app
10
+
11
+ def to_rack_app
12
+ builder = Rack::Builder.new
13
+ builder.run app
14
+ builder.to_app
15
+ end
8
16
  }.new
@@ -0,0 +1,29 @@
1
+ mapped_app = Class.new do
2
+ include Hobby
3
+
4
+ def initialize name
5
+ @name = name
6
+ end
7
+
8
+ get { "The name is #{@name}." }
9
+ end
10
+
11
+ mapping_app = Class.new do
12
+ include Hobby
13
+
14
+ map '/first', mapped_app.new('A')
15
+ map '/second', mapped_app.new('B')
16
+
17
+ def initialize routes = {}
18
+ routes.each do |route, app|
19
+ map route, app
20
+ end
21
+ end
22
+ end
23
+
24
+ map '/1', mapping_app.new
25
+
26
+ routes = { '/third' => mapped_app.new('C') }
27
+ map '/2', mapping_app.new(routes)
28
+
29
+ map '/3', mapping_app.new
@@ -0,0 +1,16 @@
1
+ require 'json'
2
+
3
+ app = Class.new do
4
+ include Hobby
5
+ get { 'hello world'.to_json }
6
+
7
+ use Rack::ContentType, 'application/html'
8
+
9
+ def initialize middleware_with_arguments = nil
10
+ use *middleware_with_arguments if middleware_with_arguments
11
+ end
12
+ end
13
+
14
+ map '/1', app.new
15
+ map '/2', app.new([Rack::ContentType, 'application/json'])
16
+ map '/3', app.new
@@ -0,0 +1,7 @@
1
+ require 'json'
2
+
3
+ get { 'hello world'.to_json }
4
+
5
+ def initialize
6
+ use Rack::ContentType, 'application/json'
7
+ end
@@ -12,8 +12,34 @@ if defined? Mutant
12
12
  integration.all_tests
13
13
  end
14
14
  end
15
+
16
+ class Mutant::Isolation::Fork
17
+ def result
18
+ yield
19
+ end
20
+ end
21
+
22
+ class Mutant::Loader
23
+ def call
24
+ source = Unparser.unparse node
25
+
26
+ puts <<~S
27
+ Current mutantion:
28
+ #{source}
29
+ S
30
+
31
+ kernel.eval source, binding, subject.source_path.to_s, subject.source_line
32
+ end
33
+ end
34
+ end
35
+
36
+ module EnvFor
37
+ def env_for path, verb = 'GET'
38
+ {'REQUEST_METHOD' => verb, 'PATH_INFO' => path }
39
+ end
15
40
  end
16
41
 
17
42
  RSpec.configure do |config|
18
43
  config.expect_with :rspec, :minitest
44
+ config.include EnvFor
19
45
  end
@@ -2,10 +2,6 @@ module RouterMatchers
2
2
  extend self
3
3
  SOME_ROUTE = ->{:some_route}
4
4
 
5
- def env_for path, verb = 'GET'
6
- {'REQUEST_METHOD' => verb, 'PATH_INFO' => path }
7
- end
8
-
9
5
  def add_routes *routes
10
6
  routes.each { |route| subject.add_route 'GET', route, &SOME_ROUTE }
11
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hobby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anatoly Chernow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-23 00:00:00.000000000 Z
11
+ date: 2017-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -39,6 +39,7 @@ files:
39
39
  - lib/hobby.rb
40
40
  - lib/hobby/helpers.rb
41
41
  - lib/hobby/router.rb
42
+ - lib/hobby/router/builder.rb
42
43
  - lib/hobby/router/route.rb
43
44
  - lib/hobby/router/routes.rb
44
45
  - readme.adoc
@@ -49,10 +50,14 @@ files:
49
50
  - spec/apps/Halting.rb
50
51
  - spec/apps/Main.rb
51
52
  - spec/apps/Map.rb
53
+ - spec/apps/MapInsideInitialize.rb
52
54
  - spec/apps/Nested.rb
53
55
  - spec/apps/OneRouteRouter.rb
54
56
  - spec/apps/Status.rb
57
+ - spec/apps/UnshareableRouterMaps.rb
58
+ - spec/apps/UnshareableRouterUses.rb
55
59
  - spec/apps/Use.rb
60
+ - spec/apps/UseInsideInitialize.rb
56
61
  - spec/apps/WithoutPath.rb
57
62
  - spec/helper.rb
58
63
  - spec/router_matchers.rb
@@ -77,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
82
  version: '0'
78
83
  requirements: []
79
84
  rubyforge_project:
80
- rubygems_version: 2.6.11
85
+ rubygems_version: 2.6.13
81
86
  signing_key:
82
87
  specification_version: 4
83
88
  summary: A minimal DSL over rack
@@ -89,10 +94,14 @@ test_files:
89
94
  - spec/apps/Halting.rb
90
95
  - spec/apps/Main.rb
91
96
  - spec/apps/Map.rb
97
+ - spec/apps/MapInsideInitialize.rb
92
98
  - spec/apps/Nested.rb
93
99
  - spec/apps/OneRouteRouter.rb
94
100
  - spec/apps/Status.rb
101
+ - spec/apps/UnshareableRouterMaps.rb
102
+ - spec/apps/UnshareableRouterUses.rb
95
103
  - spec/apps/Use.rb
104
+ - spec/apps/UseInsideInitialize.rb
96
105
  - spec/apps/WithoutPath.rb
97
106
  - spec/helper.rb
98
107
  - spec/router_matchers.rb