wire-framework 0.1.4.26 → 0.1.5
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 +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
|