wire-framework 0.1.4.26 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +201 -339
- data/README.md +16 -0
- data/lib/app.rb +38 -19
- data/lib/app/cache.rb +93 -84
- data/lib/app/db.rb +195 -196
- data/lib/app/file.rb +76 -72
- data/lib/app/history.rb +50 -49
- data/lib/app/history/svn.rb +43 -24
- data/lib/app/login.rb +23 -8
- data/lib/app/render.rb +16 -105
- data/lib/app/render/document.rb +57 -40
- data/lib/app/render/editor.rb +59 -44
- data/lib/app/render/error.rb +55 -35
- data/lib/app/render/instant.rb +71 -60
- data/lib/app/render/page.rb +104 -79
- data/lib/app/render/partial.rb +120 -99
- data/lib/app/render/style.rb +56 -42
- data/lib/app/repo.rb +141 -135
- data/lib/app/repo/svn.rb +203 -177
- data/lib/closet.rb +104 -92
- data/lib/closet/auth.rb +37 -53
- data/lib/closet/config.rb +34 -0
- data/lib/closet/context.rb +142 -98
- data/lib/closet/renderer.rb +44 -41
- data/lib/wire.rb +28 -16
- metadata +11 -40
- data/lib/closet/resource.rb +0 -20
data/lib/app/repo/svn.rb
CHANGED
@@ -1,185 +1,211 @@
|
|
1
|
-
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
2
17
|
require 'nori'
|
3
18
|
require 'fileutils'
|
19
|
+
require_relative '../repo'
|
4
20
|
|
5
21
|
# Force Nori to convert tag names to Symbols
|
6
22
|
$nori = Nori.new :convert_tags_to => lambda { |tag| tag.snakecase.to_sym }
|
7
23
|
|
8
24
|
module Repo
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
25
|
+
# Repo::SVN is a connector for svnserve
|
26
|
+
# @author Bryan T. Meyers
|
27
|
+
module SVN
|
28
|
+
extend Repo
|
29
|
+
|
30
|
+
# Make a new SVN repo
|
31
|
+
# @param [String] path the path to the repositories
|
32
|
+
# @param [String] repo the new repo name
|
33
|
+
# @return [Integer] status code
|
34
|
+
def self.do_create_file(path, repo)
|
35
|
+
result = 200
|
36
|
+
`svnadmin create #{path}/#{repo}`
|
37
|
+
if $?.exitstatus != 0
|
38
|
+
return 500
|
39
|
+
end
|
40
|
+
|
41
|
+
if $?.exitstatus != 0
|
42
|
+
500
|
43
|
+
else
|
44
|
+
result
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Read a single file
|
49
|
+
# @param [String] rev the revision number to access
|
50
|
+
# @param [String] web the subdirectory for web content
|
51
|
+
# @param [String] path the path to the repositories
|
52
|
+
# @param [String] repo the new repo name
|
53
|
+
# @param [String] id the relative path to the file
|
54
|
+
# @return [String] the file
|
55
|
+
def self.do_read_file(rev, web, path, repo, id)
|
56
|
+
options = "--username #{$environment[:repos_user]} --password #{$environment[:repos_password]}"
|
57
|
+
if rev.nil?
|
58
|
+
rev = 'HEAD'
|
59
|
+
end
|
60
|
+
if web.nil?
|
61
|
+
body = `svn cat #{options} -r #{rev} 'svn://localhost/#{repo}/#{id}'`
|
62
|
+
else
|
63
|
+
body = `svn cat #{options} -r #{rev} 'svn://localhost/#{repo}/#{web}/#{id}'`
|
64
|
+
end
|
65
|
+
|
66
|
+
if $?.success?
|
67
|
+
body
|
68
|
+
else
|
69
|
+
500
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Read a directory listing
|
74
|
+
# @param [String] web the subdirectory for web content
|
75
|
+
# @param [String] path the path to the repositories
|
76
|
+
# @param [String] repo the new repo name
|
77
|
+
# @param [String] id the relative path to the file
|
78
|
+
# @return [Array] the directory listing
|
79
|
+
def self.do_read_listing(web, path, repo, id = nil)
|
80
|
+
options = "--username #{$environment[:repos_user]} --password #{$environment[:repos_password]}"
|
81
|
+
if web.nil?
|
82
|
+
if id.nil?
|
83
|
+
list = `svn list #{options} --xml 'svn://localhost/#{repo}'`
|
84
|
+
else
|
85
|
+
list = `svn list #{options} --xml 'svn://localhost/#{repo}/#{id}'`
|
86
|
+
end
|
87
|
+
else
|
88
|
+
if id.nil?
|
89
|
+
list = `svn list #{options} --xml 'svn://localhost/#{repo}/#{web}'`
|
90
|
+
else
|
91
|
+
list = `svn list #{options} --xml 'svn://localhost/#{repo}/#{web}/#{id}'`
|
92
|
+
end
|
93
|
+
end
|
94
|
+
unless $?.exitstatus == 0
|
95
|
+
return 404
|
96
|
+
end
|
97
|
+
list = $nori.parse(list)
|
98
|
+
list[:lists][:list][:entry]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Read Metadata for a single file
|
102
|
+
# @param [String] rev the revision number to access
|
103
|
+
# @param [String] web the subdirectory for web content
|
104
|
+
# @param [String] path the path to the repositories
|
105
|
+
# @param [String] repo the new repo name
|
106
|
+
# @param [String] id the relative path to the file
|
107
|
+
# @return [Hash] the metadata
|
108
|
+
def self.do_read_info(rev, web, path, repo, id)
|
109
|
+
options = "--username #{$environment[:repos_user]} --password #{$environment[:repos_password]}"
|
110
|
+
if rev.nil?
|
111
|
+
rev = 'HEAD'
|
112
|
+
end
|
113
|
+
if web.nil?
|
114
|
+
info = `svn info #{options} -r #{rev} --xml 'svn://localhost/#{repo}/#{id}'`
|
115
|
+
else
|
116
|
+
info = `svn info #{options} -r #{rev} --xml 'svn://localhost/#{repo}/#{web}/#{id}'`
|
117
|
+
end
|
118
|
+
|
119
|
+
unless $?.exitstatus == 0
|
120
|
+
return 404
|
121
|
+
end
|
122
|
+
info = $nori.parse(info)
|
123
|
+
info[:info][:entry]
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get a file's MIME type
|
127
|
+
# @param [String] rev the revision number to access
|
128
|
+
# @param [String] web the subdirectory for web content
|
129
|
+
# @param [String] path the path to the repositories
|
130
|
+
# @param [String] repo the new repo name
|
131
|
+
# @param [String] id the relative path to the file
|
132
|
+
# @return [String] the MIME type
|
133
|
+
def self.do_read_mime(rev, web, path, repo, id)
|
134
|
+
options = "--username #{$environment[:repos_user]} --password #{$environment[:repos_password]}"
|
135
|
+
if rev.nil?
|
136
|
+
rev = 'HEAD'
|
137
|
+
end
|
138
|
+
if web.nil?
|
139
|
+
mime = `svn propget #{options} -r #{rev} --xml svn:mime-type 'svn://localhost/#{repo}/#{id}'`
|
140
|
+
else
|
141
|
+
mime = `svn propget #{options} -r #{rev} --xml svn:mime-type 'svn://localhost/#{repo}/#{web}/#{id}'`
|
142
|
+
end
|
143
|
+
|
144
|
+
unless $?.success?
|
145
|
+
return 500
|
146
|
+
end
|
147
|
+
mime = $nori.parse(mime)
|
148
|
+
if mime[:properties].nil?
|
149
|
+
'application/octet-stream'
|
150
|
+
else
|
151
|
+
mime[:properties][:target][:property]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Update a single file
|
156
|
+
# @param [String] web the subdirectory for web content
|
157
|
+
# @param [String] path the path to the repositories
|
158
|
+
# @param [String] repo the new repo name
|
159
|
+
# @param [String] id the relative path to the file
|
160
|
+
# @param [String] content the updated file
|
161
|
+
# @param [String] message the commit message
|
162
|
+
# @param [String] mime the mime-type to set
|
163
|
+
# @param [String] user the Author of this change
|
164
|
+
# @return [Integer] status code
|
165
|
+
def self.do_update_file(web, path, repo, id, content, message, mime, user)
|
166
|
+
options = "--username #{$environment[:repos_user]} --password #{$environment[:repos_password]}"
|
167
|
+
status = 500
|
168
|
+
id = id.split('/')
|
169
|
+
id.pop
|
170
|
+
id = id.join('/')
|
171
|
+
if web.nil?
|
172
|
+
repo_path = "/tmp/svn/#{repo}/#{id}"
|
173
|
+
else
|
174
|
+
repo_path = "/tmp/svn/#{repo}/#{web}/#{id}"
|
175
|
+
end
|
176
|
+
unless Dir.exist? repo_path
|
177
|
+
FileUtils.mkdir_p(repo_path)
|
178
|
+
end
|
179
|
+
|
180
|
+
`svn checkout #{options} 'svn://localhost/#{repo}' '/tmp/svn/#{repo}'`
|
181
|
+
id = CGI.unescape(id)
|
182
|
+
if $?.exitstatus == 0
|
183
|
+
id = id.split('/')
|
184
|
+
id.pop
|
185
|
+
id = id.join('/')
|
186
|
+
if web.nil?
|
187
|
+
file_path = "/tmp/svn/#{repo}/#{id}"
|
188
|
+
else
|
189
|
+
file_path = "/tmp/svn/#{repo}/#{web}/#{id}"
|
190
|
+
end
|
191
|
+
|
192
|
+
unless Dir.exist? file_path
|
193
|
+
FileUtils.mkdir_p(file_path)
|
194
|
+
end
|
195
|
+
|
196
|
+
file = File.open(file_path, 'w+')
|
197
|
+
file.syswrite(content)
|
198
|
+
file.close
|
199
|
+
`svn add --force "/tmp/svn/#{repo}/*"`
|
200
|
+
`svn propset svn:mime-type "#{mime}" "#{file_path}"`
|
201
|
+
`svn commit #{options} -m "#{message}" "/tmp/svn/#{repo}"`
|
202
|
+
if $?.exitstatus == 0
|
203
|
+
status = 200
|
204
|
+
end
|
205
|
+
`svn propset #{options} --revprop -r HEAD svn:author '#{user}' "/tmp/svn/#{repo}"`
|
206
|
+
end
|
207
|
+
`rm -R '/tmp/svn/#{repo}'`
|
208
|
+
status
|
209
|
+
end
|
210
|
+
end
|
185
211
|
end
|
data/lib/closet.rb
CHANGED
@@ -1,106 +1,118 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2017 Bryan T. Meyers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
##
|
16
|
+
|
1
17
|
require 'rack'
|
2
|
-
require 'awesome_print'
|
3
18
|
require_relative 'app'
|
4
19
|
require_relative 'closet/auth'
|
20
|
+
require_relative 'closet/config'
|
5
21
|
require_relative 'closet/context'
|
6
|
-
require_relative 'closet/resource'
|
7
22
|
require_relative 'closet/renderer'
|
8
23
|
|
9
24
|
|
10
25
|
module Wire
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
include Wire::Auth
|
16
|
-
include Wire::Renderer
|
17
|
-
include Wire::Resource
|
26
|
+
# A Closet is a configured Wire Environment
|
27
|
+
# @author Bryan T. Meyers
|
28
|
+
class Closet
|
29
|
+
include Wire::Auth
|
18
30
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
# Create an empty Closet
|
32
|
+
# @return [Wire::Closet] the new closet
|
33
|
+
def initialize
|
34
|
+
$wire_apps = {}
|
35
|
+
$wire_editors = {}
|
36
|
+
$wire_renderers = {}
|
37
|
+
$wire_templates = {}
|
38
|
+
end
|
27
39
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
# Route a Request to the correct Wire::App
|
41
|
+
# @param [Wire::Context] context the context of this Request
|
42
|
+
# @return [Response] a Rack Response triplet, or status code
|
43
|
+
def route(context)
|
44
|
+
actions = actions_allowed(context)
|
45
|
+
if actions.include? context.action
|
46
|
+
context.config['type'].invoke(actions, context)
|
47
|
+
else
|
48
|
+
401
|
49
|
+
end
|
50
|
+
end
|
39
51
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
52
|
+
# Fulfill the current request
|
53
|
+
# @param [Hash] env the Rack environment
|
54
|
+
# @return [Response] a Rack Response triplet, or status code
|
55
|
+
def call(env)
|
56
|
+
begin
|
57
|
+
context = Wire::Context.new(env)
|
58
|
+
response = route(context)
|
59
|
+
rescue Exception => e
|
60
|
+
$stderr.puts e.message
|
61
|
+
$stderr.puts e.backtrace
|
62
|
+
response = [500, {}, e.message]
|
63
|
+
end
|
64
|
+
if response.is_a? Array
|
65
|
+
if response[2]
|
66
|
+
unless response[2].is_a? Array
|
67
|
+
response[2] = [response[2]]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
if response.is_a? Integer
|
72
|
+
response = [response, {}, []]
|
73
|
+
else
|
74
|
+
response = [200, {}, [response]]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
response
|
78
|
+
end
|
67
79
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
# A factory method for configuring a Closet
|
81
|
+
# @param [Proc] block the configuration routine
|
82
|
+
# @return [Wire::Closet] the configured Closet
|
83
|
+
def self.build
|
84
|
+
closet = Wire::Closet.new
|
85
|
+
if ENV['RACK_ENV'].eql? 'development'
|
86
|
+
$stderr.puts 'Starting Up Wire...'
|
87
|
+
$stderr.puts 'Starting Apps...'
|
88
|
+
end
|
89
|
+
Wire::App.read_configs
|
90
|
+
Wire::Renderer.read_configs
|
91
|
+
if ENV['RACK_ENV'].eql? 'development'
|
92
|
+
closet.info
|
93
|
+
end
|
94
|
+
closet
|
95
|
+
end
|
83
96
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
end
|
97
|
+
# Print out a human-readable configuration
|
98
|
+
# @return [void]
|
99
|
+
def info
|
100
|
+
$stderr.puts "Apps:\n"
|
101
|
+
$wire_apps.each do |app, config|
|
102
|
+
$stderr.puts "\u{2502}"
|
103
|
+
$stderr.puts "\u{251c} Name: #{app}"
|
104
|
+
if config['auth_handler']
|
105
|
+
$stderr.puts "\u{2502}\t\u{251c} Auth:"
|
106
|
+
$stderr.puts "\u{2502}\t\u{2502}\t\u{2514} Handler:\t#{config['auth_handler']}"
|
107
|
+
end
|
108
|
+
if config['auth_read_only']
|
109
|
+
$stderr.puts "\u{2502}\t\u{251c} Auth:"
|
110
|
+
$stderr.puts "\u{2502}\t\u{2502}\t\u{2514} Read Only:\t#{config['auth_read_only']}"
|
111
|
+
end
|
112
|
+
if config['type']
|
113
|
+
$stderr.puts "\u{2502}\t\u{2514} Type: #{config['type']}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
106
118
|
end
|