ground 0.0.1 → 0.0.2
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.
- checksums.yaml +8 -8
- data/lib/ground.rb +53 -0
- data/lib/ground/activity.rb +3 -0
- data/lib/ground/activity/create_application.rb +34 -0
- data/lib/ground/activity/hash_with_double_access.rb +28 -0
- data/lib/ground/activity/locate.rb +41 -0
- data/lib/ground/activity/log.rb +28 -0
- data/lib/ground/activity/ridge.rb +42 -0
- data/lib/ground/activity/state.rb +52 -0
- data/lib/ground/activity/validate.rb +37 -0
- data/lib/ground/mime_type.rb +23 -0
- data/lib/ground/protocol/helper.rb +19 -0
- data/lib/ground/protocol/render.rb +59 -0
- data/lib/ground/protocol/render/template.rb +26 -0
- data/lib/ground/protocol/verb.rb +17 -0
- data/lib/ground/rack/base_rack.rb +57 -0
- data/lib/ground/rack/url_suffix.rb +42 -0
- data/test/ground_set_test.rb +13 -0
- data/test/hash_with_double_access_test.rb +31 -0
- data/test/locate_test.rb +23 -0
- data/test/ridge_test.rb +15 -0
- data/test/routes.rb +13 -0
- data/test/test_helper.rb +11 -0
- data/test/validate_test.rb +10 -0
- metadata +26 -34
- data/README.md +0 -114
- data/Rakefile +0 -8
- data/licenses.txt +0 -21
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MjIwZmFhYTk5YWI3NTk3ZGMyMjExNTczMzAyOTJhZWE5MjVjZjNjYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZWI0NzM1OTY3YjFhN2NiNmMyMzRlOTk3NzljM2YwYmM3MjY3NjJiZg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDIzODkyN2ZjMmJiNTMzZTEzMjI2MDVmYWQ1MTVlODhkOTY3OTYxNzliMTUz
|
10
|
+
NTJlMDBkNGQzNmIyYmU3NGQ4YmQzZjg0MjA5MGI4YzNmNDhiOWQ5YTVjMTc3
|
11
|
+
NzQ1MjQyM2Y0ZmY3MjVkMGJjYmJhMmVkYzFhMDM4NWExMTQ5ODE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MWQ0MmRkNTQ0NGJjNmViY2NmNmQ5YWFiZTNmYWE5YzU0OWIzMmQ5ZGQ0ZDg1
|
14
|
+
MjM1NjI0Yzk4ZTk1Y2YyZWVjNGNmNTQ0NGE1NjY0MWFjNGUyNmMzNmRiODNj
|
15
|
+
NzM2M2Q1ZGUwM2Q2NjZiMWM3NTJkNzY3MDZiMzdlNWQ0N2QyMzY=
|
data/lib/ground.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'dun'
|
2
|
+
require 'rack'
|
3
|
+
require 'logger'
|
4
|
+
require 'ground/activity'
|
5
|
+
require 'ground/mime_type'
|
6
|
+
require 'ground/protocol/render'
|
7
|
+
require 'ground/protocol/render/template'
|
8
|
+
require 'ground/activity/ridge'
|
9
|
+
require 'ground/protocol/verb'
|
10
|
+
require 'ground/protocol/helper'
|
11
|
+
require 'ground/activity/log'
|
12
|
+
require 'ground/activity/locate'
|
13
|
+
require 'ground/activity/state'
|
14
|
+
require 'ground/activity/validate'
|
15
|
+
require 'ground/rack/url_suffix'
|
16
|
+
require 'ground/rack/base_rack'
|
17
|
+
require 'ground/activity/hash_with_double_access'
|
18
|
+
require 'ground/activity/create_application'
|
19
|
+
|
20
|
+
|
21
|
+
module Ground
|
22
|
+
extend Protocol::Verb
|
23
|
+
extend Protocol::Helper
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
MetaG = self
|
28
|
+
|
29
|
+
def set(attr, value)
|
30
|
+
MetaG.class_eval { attr_reader attr }
|
31
|
+
instance_variable_set "@#{attr}", value
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
if not Kernel.method_defined?('Ground')
|
39
|
+
Kernel.class_eval do
|
40
|
+
def Ground(app_name = nil, &p)
|
41
|
+
if app_name
|
42
|
+
Ground::CreateApplication(name: app_name, &p)
|
43
|
+
else
|
44
|
+
Ground.instance_eval &p
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Ground do
|
51
|
+
set :logger, ::Logger.new(STDOUT)
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Ground
|
3
|
+
|
4
|
+
class CreateApplication < Activity
|
5
|
+
data_reader :name
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
super
|
9
|
+
@middlewares = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(&p)
|
13
|
+
app = Class.new(Ground::BaseRack)
|
14
|
+
instance_eval &p
|
15
|
+
app_with_middlewares = pack_middlewares_to_app app
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def use(middleware, *args, &p)
|
21
|
+
@middlewares << lambda {|app| middleware.new(app, *args, &p)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def pack_middlewares_to_app(app)
|
25
|
+
app_with_middlewares = app.new
|
26
|
+
@middlewares.reverse.each {|middleware|
|
27
|
+
app_with_middlewares = middleware.call(app_with_middlewares)
|
28
|
+
}
|
29
|
+
app_with_middlewares
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class HashWithDoubleAccess < Ground::Activity
|
2
|
+
|
3
|
+
def call
|
4
|
+
str_h = access_with(data, :to_s)
|
5
|
+
sym_h = access_with(data, :to_sym)
|
6
|
+
sym_h.merge str_h
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def access_with(d, m)
|
13
|
+
tmp_h = {}
|
14
|
+
d.each {|k, v|
|
15
|
+
if v.is_a? Hash
|
16
|
+
tmp_h[k.send(m)] = access_with(v, m)
|
17
|
+
elsif v.is_a? Array
|
18
|
+
v_dup = v.dup
|
19
|
+
v_dup.each_with_index {|item, index| v_dup[index] = access_with(item, m) if item.is_a? Hash}
|
20
|
+
tmp_h[k.send(m)] = v_dup
|
21
|
+
else
|
22
|
+
tmp_h[k.send(m)] = v
|
23
|
+
end
|
24
|
+
}
|
25
|
+
tmp_h
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ground
|
2
|
+
|
3
|
+
class Locate < Activity
|
4
|
+
data_reader :verb, :path
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
super
|
8
|
+
@verb = verb.upcase
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
path_segs = split_to_segs(path)
|
13
|
+
|
14
|
+
length_match_routes = Ridge.routes[verb].select {|route|
|
15
|
+
route_segs = split_to_segs(route[0])
|
16
|
+
route_segs.size == path_segs.size
|
17
|
+
}
|
18
|
+
|
19
|
+
length_match_routes.detect {|route|
|
20
|
+
match = true
|
21
|
+
route_segs = split_to_segs(route[0])
|
22
|
+
route_segs.each_with_index {|route_seg, index|
|
23
|
+
match = (path_segs[index] == route_seg) if not route_seg =~ /^:\w+/
|
24
|
+
break if not match
|
25
|
+
}
|
26
|
+
match
|
27
|
+
}
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def split_to_segs(path)
|
34
|
+
segs = path.split('/')
|
35
|
+
segs[0] = '/'
|
36
|
+
segs
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ground
|
2
|
+
class Log < Activity
|
3
|
+
set :format, %{(%0.4fs) %s %s %s %s %s %s\n}
|
4
|
+
|
5
|
+
data_reader :time
|
6
|
+
data_reader :request_method, :path_info
|
7
|
+
data_reader :state
|
8
|
+
data_reader :response
|
9
|
+
data_reader :params
|
10
|
+
|
11
|
+
def call
|
12
|
+
logger.info format % [time,
|
13
|
+
request_method,
|
14
|
+
path_info,
|
15
|
+
state,
|
16
|
+
response[0],
|
17
|
+
response[1],
|
18
|
+
"Parameters: #{params.select{|k, _| k.is_a? Symbol}}"]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def logger
|
24
|
+
Ground.logger
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Ground
|
3
|
+
|
4
|
+
class Ridge < Activity
|
5
|
+
|
6
|
+
data_reader :path, :verb, :state
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
attr_reader :routes, :states
|
11
|
+
|
12
|
+
# 路由节点的结构 [path, state]
|
13
|
+
def route(verb, path, state)
|
14
|
+
|
15
|
+
@routes ||= {'GET' => [], 'POST' => []}
|
16
|
+
@states ||= []
|
17
|
+
|
18
|
+
@routes[verb] << [path, state]
|
19
|
+
@states << state if not @states.include?(state)
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(data)
|
26
|
+
super
|
27
|
+
@verb = verb.upcase
|
28
|
+
end
|
29
|
+
|
30
|
+
def call
|
31
|
+
route(state)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def route(state)
|
37
|
+
self.class.route(verb, path, state)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Ground
|
2
|
+
|
3
|
+
class State < Activity
|
4
|
+
|
5
|
+
include Ground::Protocol::Render
|
6
|
+
include Ground::Protocol::Render::Template
|
7
|
+
|
8
|
+
data_reader :request, :params
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def path(*args)
|
13
|
+
paths = []
|
14
|
+
route = detect_route('GET') || detect_route('POST')
|
15
|
+
|
16
|
+
route[0].split('/').each_with_index {|route_seg, index|
|
17
|
+
paths[index] = (route_seg =~ /^:\w+/ ? args.shift : route_seg)
|
18
|
+
}
|
19
|
+
paths << '/' if paths.size == 0
|
20
|
+
paths.join('/')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def detect_route(verb)
|
25
|
+
Ridge.routes[verb.to_s.upcase].detect {|route| route[1] == self}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def response
|
34
|
+
@response ||= ::Rack::Response.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def session
|
38
|
+
@session ||= request.session
|
39
|
+
end
|
40
|
+
|
41
|
+
# check Ground::MimeType for all mime type abb defined in ground
|
42
|
+
def request_accept?(mime_type_abb)
|
43
|
+
Ground::MimeType[request_accepts.first] == mime_type_abb
|
44
|
+
end
|
45
|
+
|
46
|
+
def request_accepts
|
47
|
+
@request_accepts ||= request.env['HTTP_ACCEPT'].split(', ')
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Ground
|
2
|
+
class Validate < Activity
|
3
|
+
|
4
|
+
attr_reader :errors, :validations
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
super
|
8
|
+
@errors = {}
|
9
|
+
@validations = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
execute_validations
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def validates(attr, error_msg, &p)
|
19
|
+
validation = validations.detect {|validation|
|
20
|
+
validation[0] == attr and validation[1] == error_msg
|
21
|
+
} || [attr, error_msg, p]
|
22
|
+
validations << validation
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute_validations
|
26
|
+
validations.each {|validation|
|
27
|
+
attr, error_msg, p = validation
|
28
|
+
if !p.call
|
29
|
+
errors[attr] ||= []
|
30
|
+
errors[attr] << error_msg if !errors[attr].include?(error_msg)
|
31
|
+
end
|
32
|
+
}
|
33
|
+
errors
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ground
|
2
|
+
MimeType = {
|
3
|
+
"*/*" => :all,
|
4
|
+
"text/plain" => :text,
|
5
|
+
"text/html" => :html,
|
6
|
+
"application/xhtml+xml" => :html,
|
7
|
+
"text/javascript" => :js,
|
8
|
+
"application/javascript" => :js,
|
9
|
+
"application/x-javascript" => :js,
|
10
|
+
"text/calendar" => :ics,
|
11
|
+
"text/csv" => :csv,
|
12
|
+
"application/xml" => :xml,
|
13
|
+
"text/xml" => :xml,
|
14
|
+
"application/x-xml" => :xml,
|
15
|
+
"text/yaml" => :yaml,
|
16
|
+
"application/x-yaml" => :yaml,
|
17
|
+
"application/rss+xml" => :rss,
|
18
|
+
"application/atom+xml" => :atom,
|
19
|
+
"application/json" => :json,
|
20
|
+
"text/x-json" => :json,
|
21
|
+
"text/markdown" => :md
|
22
|
+
}
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ground::Protocol
|
2
|
+
|
3
|
+
module Helper
|
4
|
+
|
5
|
+
Ridge = Ground::Ridge
|
6
|
+
|
7
|
+
def help(*args, &p)
|
8
|
+
if args.first == :all_states
|
9
|
+
help Ground::State, &p
|
10
|
+
else
|
11
|
+
args.each {|obj|
|
12
|
+
obj.class_eval &p if block_given?
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Ground::Protocol
|
2
|
+
module Render
|
3
|
+
|
4
|
+
include ::Rack::Utils
|
5
|
+
|
6
|
+
def json(content, status = 200)
|
7
|
+
response_with(content, status, 'application/json; charset=UTF-8')
|
8
|
+
end
|
9
|
+
|
10
|
+
def xml(content, status = 200)
|
11
|
+
response_with(content, status, 'application/xml; charset=UTF-8')
|
12
|
+
end
|
13
|
+
|
14
|
+
def html(content, status = 200)
|
15
|
+
response_with(content, status, 'text/html; charset=UTF-8')
|
16
|
+
end
|
17
|
+
|
18
|
+
def text(content, status = 200)
|
19
|
+
response_with(content, status, 'text/plain; charset=UTF-8')
|
20
|
+
end
|
21
|
+
|
22
|
+
def non_found(content)
|
23
|
+
response_as 404, content
|
24
|
+
end
|
25
|
+
|
26
|
+
def forbid(content = nil)
|
27
|
+
response_as 403, content
|
28
|
+
end
|
29
|
+
|
30
|
+
def not_accept(content = nil)
|
31
|
+
response_as 406, content
|
32
|
+
end
|
33
|
+
|
34
|
+
def unauthorized(content = nil)
|
35
|
+
response_as 401, content
|
36
|
+
end
|
37
|
+
|
38
|
+
def redirect(target, status = 302)
|
39
|
+
response.status = status
|
40
|
+
response['Location'] = target
|
41
|
+
response.finish
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def response_as(status, content=nil)
|
47
|
+
content ||= HTTP_STATUS_CODES[status]
|
48
|
+
text content, status
|
49
|
+
end
|
50
|
+
|
51
|
+
def response_with(content, status, content_type)
|
52
|
+
response['Content-Type'] = content_type
|
53
|
+
response.status = status
|
54
|
+
response.write(content)
|
55
|
+
response.finish
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ground::Protocol
|
2
|
+
module Render
|
3
|
+
module Template
|
4
|
+
|
5
|
+
def haml(path, &p)
|
6
|
+
template = Tilt::HamlTemplate.new(File.join(Ground.views, "#{path}.haml"))
|
7
|
+
template.render self, &p
|
8
|
+
end
|
9
|
+
|
10
|
+
def slim(path, &p)
|
11
|
+
template = Slim::Template.new(File.join(Ground.views, "#{path}.slim"))
|
12
|
+
template.render self, &p
|
13
|
+
end
|
14
|
+
|
15
|
+
def erb(file, &p)
|
16
|
+
template = ERB.new(File.read(File.join(Ground.root, file)))
|
17
|
+
template.result(binding)
|
18
|
+
end
|
19
|
+
|
20
|
+
def plain(file, &p)
|
21
|
+
File.read(File.join(Ground.root, file))
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Ground::Protocol
|
2
|
+
module Verb
|
3
|
+
|
4
|
+
def get(path, state)
|
5
|
+
ridge path: path, verb: 'get', state: state
|
6
|
+
end
|
7
|
+
|
8
|
+
def post(path, state)
|
9
|
+
ridge path: path, verb: 'post', state: state
|
10
|
+
end
|
11
|
+
|
12
|
+
def ridge(data)
|
13
|
+
Ground::Ridge data
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Ground
|
2
|
+
class BaseRack
|
3
|
+
|
4
|
+
attr_reader :env
|
5
|
+
attr_reader :request
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
start_time = Time.now
|
9
|
+
set_request Rack::Request.new(env)
|
10
|
+
|
11
|
+
route, state = Ground::Locate(verb: request_method, path: path_info)
|
12
|
+
return non_exist_route if state.nil?
|
13
|
+
|
14
|
+
params = get_params route
|
15
|
+
response = state << {request: request, params: params}
|
16
|
+
time = Time.now - start_time
|
17
|
+
Ground::Log({
|
18
|
+
time: time,
|
19
|
+
request_method: request_method,
|
20
|
+
path_info: request.env['REQUEST_PATH'],
|
21
|
+
state: state,
|
22
|
+
response: response,
|
23
|
+
params: params
|
24
|
+
})
|
25
|
+
|
26
|
+
response
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def set_request request
|
32
|
+
@request = request
|
33
|
+
end
|
34
|
+
|
35
|
+
def request_method
|
36
|
+
request.request_method
|
37
|
+
end
|
38
|
+
|
39
|
+
def path_info
|
40
|
+
request.path_info
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_params(route)
|
44
|
+
p = {}
|
45
|
+
path_segs = request.path_info.split('/')
|
46
|
+
route.split('/').each_with_index {|route_seg, index|
|
47
|
+
p[route_seg.sub(':', '')] = path_segs[index] if route_seg =~ /^:\w+/
|
48
|
+
}
|
49
|
+
HashWithDoubleAccess p.merge(request.params)
|
50
|
+
end
|
51
|
+
|
52
|
+
def non_exist_route
|
53
|
+
Rack::Response.new(["Non Exist Route: #{request_method} #{path_info}"], 500)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Ground
|
2
|
+
|
3
|
+
# When request's path contains a suffix like json, xml etc,
|
4
|
+
# it will git rid of the suffix and set the accept header
|
5
|
+
# by the suffix
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# get '/book/:id.json'
|
9
|
+
#
|
10
|
+
# the request path '/book/2.json' will become '/book/2', and
|
11
|
+
# the request accept header will be added a 'application/json'
|
12
|
+
# mime type
|
13
|
+
class UrlSuffix
|
14
|
+
|
15
|
+
def initialize(app)
|
16
|
+
@app = app
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
suffix = /\.(\w+)$/
|
21
|
+
if env['PATH_INFO'].match(suffix)
|
22
|
+
accept_type = suffix_to_accept_type $1
|
23
|
+
if accept_type
|
24
|
+
env['HTTP_ACCEPT'] = "#{accept_type}, #{env['HTTP_ACCEPT']}"
|
25
|
+
env['PATH_INFO'] = env['PATH_INFO'].sub(suffix, '')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@app.call(env)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def suffix_to_accept_type(suffix)
|
36
|
+
accept = Ground::MimeType.detect {|_, v| v == suffix.to_sym}
|
37
|
+
accept[0] if accept
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
class HashWithDoubleAccessTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_double_access
|
7
|
+
h = {:a => 1, 'b' => 2, 'c' => {'d' => 4, :e => 5}, 'g' => [1,2,3], 'h' => [{'a' => 1, 'cc' => [{'b' => 2}, {'c' => 3}]}, {'b' => 5}]}
|
8
|
+
|
9
|
+
hd = HashWithDoubleAccess h
|
10
|
+
|
11
|
+
assert_equal hd[:a], 1
|
12
|
+
assert_equal hd['a'], 1
|
13
|
+
assert_equal hd['c']['d'], 4
|
14
|
+
assert_equal hd[:c][:d], 4
|
15
|
+
assert_equal hd['c']['e'], 5
|
16
|
+
assert_equal hd[:c][:e], 5
|
17
|
+
|
18
|
+
assert_equal hd[:c]['d'], nil
|
19
|
+
assert_equal hd[:c]['e'], nil
|
20
|
+
assert_equal hd['c'][:e], nil
|
21
|
+
assert_equal hd['c'][:d], nil
|
22
|
+
|
23
|
+
assert_equal hd[:h].size, 2
|
24
|
+
assert_equal hd[:h][0][:a], 1
|
25
|
+
assert_equal hd[:h][1][:b], 5
|
26
|
+
assert_equal hd['h'][0][:a], nil
|
27
|
+
assert_equal hd['h'][1][:b], nil
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/test/locate_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'routes'
|
3
|
+
|
4
|
+
class LocateTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_locate
|
7
|
+
location = Ground::Locate(verb: 'get', path: '/')
|
8
|
+
assert_equal location, ['/', SiteIndex]
|
9
|
+
|
10
|
+
location = Ground::Locate(verb: 'get', path: '/books')
|
11
|
+
assert_equal location, ['/books', BooksIndex]
|
12
|
+
|
13
|
+
location = Ground::Locate(verb: 'get', path: '/book/2')
|
14
|
+
assert_equal location, ['/book/:id', BookShow]
|
15
|
+
|
16
|
+
location = Ground::Locate(verb: 'post', path: '/book/3/comments')
|
17
|
+
assert_equal location, ['/book/:id/comments', BookComments]
|
18
|
+
|
19
|
+
location = Ground::Locate(verb: 'get', path: '/jim/notes')
|
20
|
+
assert_equal location, ['/:username/notes', UserNotes]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/test/ridge_test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
require 'routes'
|
4
|
+
|
5
|
+
class RidgeTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_state_path
|
8
|
+
assert_equal SiteIndex.path, '/'
|
9
|
+
assert_equal BookShow.path(1), '/book/1'
|
10
|
+
assert_equal BooksIndex.path, '/books'
|
11
|
+
assert_equal BookComments.path(1), '/book/1/comments'
|
12
|
+
assert_equal UserNotes.path('jim'), '/jim/notes'
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
SiteIndex = Class.new(Ground::State)
|
2
|
+
BooksIndex = Class.new(Ground::State)
|
3
|
+
BookShow = Class.new(Ground::State)
|
4
|
+
BookComments = Class.new(Ground::State)
|
5
|
+
UserNotes = Class.new(Ground::State)
|
6
|
+
|
7
|
+
Ground::Ridge(verb: 'get', path: '/', state: SiteIndex)
|
8
|
+
Ground::Ridge(verb: 'get', path: '/book/:id', state: BookShow)
|
9
|
+
Ground::Ridge(verb: 'get', path: '/books', state: BooksIndex)
|
10
|
+
Ground::Ridge(verb: 'post', path: '/book/:id/comments', state: BookComments)
|
11
|
+
Ground::Ridge(verb: 'get', path: '/:username/notes', state: UserNotes)
|
12
|
+
|
13
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
testdir = File.dirname(__FILE__)
|
4
|
+
$LOAD_PATH.unshift testdir unless $LOAD_PATH.include?(testdir)
|
5
|
+
|
6
|
+
libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
|
7
|
+
$LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
|
8
|
+
|
9
|
+
require 'ground'
|
10
|
+
|
11
|
+
|
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ground
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- jgm
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2014-06-25 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: dun
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ! '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ! '>='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rack
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ! '>='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ! '>='
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
12
|
+
dependencies: []
|
41
13
|
description: ! ' A simple ruby web framework
|
42
14
|
|
43
15
|
'
|
@@ -46,9 +18,29 @@ executables: []
|
|
46
18
|
extensions: []
|
47
19
|
extra_rdoc_files: []
|
48
20
|
files:
|
49
|
-
-
|
50
|
-
-
|
51
|
-
-
|
21
|
+
- lib/ground.rb
|
22
|
+
- lib/ground/activity.rb
|
23
|
+
- lib/ground/activity/create_application.rb
|
24
|
+
- lib/ground/activity/hash_with_double_access.rb
|
25
|
+
- lib/ground/activity/locate.rb
|
26
|
+
- lib/ground/activity/log.rb
|
27
|
+
- lib/ground/activity/ridge.rb
|
28
|
+
- lib/ground/activity/state.rb
|
29
|
+
- lib/ground/activity/validate.rb
|
30
|
+
- lib/ground/mime_type.rb
|
31
|
+
- lib/ground/protocol/helper.rb
|
32
|
+
- lib/ground/protocol/render.rb
|
33
|
+
- lib/ground/protocol/render/template.rb
|
34
|
+
- lib/ground/protocol/verb.rb
|
35
|
+
- lib/ground/rack/base_rack.rb
|
36
|
+
- lib/ground/rack/url_suffix.rb
|
37
|
+
- test/ground_set_test.rb
|
38
|
+
- test/hash_with_double_access_test.rb
|
39
|
+
- test/locate_test.rb
|
40
|
+
- test/ridge_test.rb
|
41
|
+
- test/routes.rb
|
42
|
+
- test/test_helper.rb
|
43
|
+
- test/validate_test.rb
|
52
44
|
homepage: https://github.com/baya/ground
|
53
45
|
licenses:
|
54
46
|
- MIT
|
data/README.md
DELETED
@@ -1,114 +0,0 @@
|
|
1
|
-
# Ground
|
2
|
-
Ground is a web framework, no controller, no model, no views and no mvc. Most of the ground codes are inherited from
|
3
|
-
[Dun::Activity](https://github.com/baya/dun/blob/master/lib/dun.rb). So ground is a high unified web framework.
|
4
|
-
|
5
|
-
## states
|
6
|
-
|
7
|
-
```ruby
|
8
|
-
class SiteIndex < Ground::State
|
9
|
-
def call
|
10
|
-
text 'Site Index'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module Book
|
15
|
-
State = Ground::State
|
16
|
-
|
17
|
-
class Index < State
|
18
|
-
def call
|
19
|
-
@books = Book.all
|
20
|
-
html erb('views/book/index.html.erb')
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class Show < State
|
25
|
-
def call
|
26
|
-
@book = Book.find(params[:id])
|
27
|
-
html erb('views/book/show.html.erb')
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
class Create < State
|
32
|
-
def call
|
33
|
-
@book = Book.create(params[:book])
|
34
|
-
rediret '/books'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
```
|
40
|
-
|
41
|
-
## routes
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
Ground do
|
45
|
-
get '/', SiteIndex
|
46
|
-
get '/books', Book::Index
|
47
|
-
get '/book/:id', Book::Show
|
48
|
-
post '/book', Book::Create
|
49
|
-
end
|
50
|
-
```
|
51
|
-
|
52
|
-
## route helpers
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
SiteIndex.path # '/'
|
56
|
-
Book::Index.path # '/books'
|
57
|
-
Book::Show.path(2) # '/book/2'
|
58
|
-
Book::Create.path # '/book'
|
59
|
-
```
|
60
|
-
|
61
|
-
## sets
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
Ground do
|
65
|
-
set :env, (ENV['RACK_ENV'] || 'development').to_s
|
66
|
-
set :logger, ::Logger.new(STDOUT)
|
67
|
-
en
|
68
|
-
|
69
|
-
Ground.env #=> development
|
70
|
-
Ground.logger.info("debug+++")
|
71
|
-
```
|
72
|
-
|
73
|
-
## helpers
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
# Book::Index, Book::Show will hava instance method 'hello_ground'
|
77
|
-
Ground do
|
78
|
-
help Book::Index, Book::Show do
|
79
|
-
|
80
|
-
def hello_ground
|
81
|
-
'hello ground'
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
86
|
-
```
|
87
|
-
|
88
|
-
## create a ground app
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
module BookStore
|
92
|
-
App = Ground 'book store' do
|
93
|
-
use Rack::ShowExceptions
|
94
|
-
use Rack::Static, urls: ['/js', '/css', '/lib', '/partials'], :root => "lib/app"
|
95
|
-
...
|
96
|
-
end
|
97
|
-
end
|
98
|
-
```
|
99
|
-
|
100
|
-
## config.ru
|
101
|
-
|
102
|
-
``` ruby
|
103
|
-
$: << '.'
|
104
|
-
require 'app'
|
105
|
-
run BookStore::App
|
106
|
-
```
|
107
|
-
|
108
|
-
## Apps using ground
|
109
|
-
|
110
|
-
* [SceneMaster](https://github.com/baya/SceneMaster)
|
111
|
-
* [Description](https://github.com/baya/description)
|
112
|
-
* [Gstar](https://github.com/baya/Gstar)
|
113
|
-
|
114
|
-
|
data/Rakefile
DELETED
data/licenses.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) <year> <copyright holders>
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|