spiderfw 0.6.23 → 0.6.24
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.
- data/CHANGELOG +10 -1
- data/README.rdoc +1 -1
- data/VERSION +1 -1
- data/apps/config_editor/_init.rb +1 -2
- data/apps/config_editor/controllers/config_editor_controller.rb +1 -7
- data/apps/core/admin/controllers/admin_controller.rb +1 -1
- data/apps/core/admin/public/css/sass/admin.css +35 -31
- data/apps/core/admin/public/sass/admin.scss +6 -1
- data/apps/core/components/widgets/crud/crud.shtml +2 -2
- data/apps/core/components/widgets/table/table.rb +5 -5
- data/apps/core/forms/tags/element_row.erb +15 -10
- data/apps/core/forms/widgets/form/form.rb +35 -22
- data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +2 -2
- data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +2 -2
- data/apps/core/forms/widgets/inputs/file_input/file_input.shtml +2 -2
- data/apps/core/forms/widgets/inputs/html_area/html_area.shtml +2 -2
- data/apps/core/forms/widgets/inputs/input/input.shtml +2 -2
- data/apps/core/forms/widgets/inputs/password/password.shtml +2 -2
- data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +1 -1
- data/apps/core/forms/widgets/inputs/select/select.shtml +2 -2
- data/apps/core/forms/widgets/inputs/text/text.shtml +2 -2
- data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +2 -2
- data/apps/core/forms/widgets/inputs/time_span/time_span.shtml +1 -1
- data/blueprints/home/config.ru +8 -0
- data/lib/spiderfw/app.rb +416 -224
- data/lib/spiderfw/cmd/commands/app.rb +243 -239
- data/lib/spiderfw/cmd/commands/cert.rb +421 -417
- data/lib/spiderfw/cmd/commands/config.rb +85 -82
- data/lib/spiderfw/cmd/commands/console.rb +64 -40
- data/lib/spiderfw/cmd/commands/content.rb +29 -25
- data/lib/spiderfw/cmd/commands/create.rb +58 -54
- data/lib/spiderfw/cmd/commands/model.rb +118 -114
- data/lib/spiderfw/cmd/commands/setup.rb +55 -51
- data/lib/spiderfw/cmd/commands/test.rb +63 -59
- data/lib/spiderfw/cmd/commands/webserver.rb +56 -51
- data/lib/spiderfw/config/options/spider.rb +4 -3
- data/lib/spiderfw/controller/controller.rb +2 -0
- data/lib/spiderfw/controller/http_controller.rb +1 -2
- data/lib/spiderfw/controller/mixins/static_content.rb +3 -3
- data/lib/spiderfw/controller/mixins/visual.rb +30 -15
- data/lib/spiderfw/controller/response.rb +84 -0
- data/lib/spiderfw/controller/session/file_session.rb +2 -2
- data/lib/spiderfw/http/adapters/rack.rb +12 -13
- data/lib/spiderfw/http/server.rb +80 -46
- data/lib/spiderfw/i18n/cldr.rb +6 -9
- data/lib/spiderfw/model/base_model.rb +103 -23
- data/lib/spiderfw/model/condition.rb +110 -25
- data/lib/spiderfw/model/mappers/db_mapper.rb +14 -6
- data/lib/spiderfw/model/mappers/mapper.rb +440 -197
- data/lib/spiderfw/model/model.rb +105 -21
- data/lib/spiderfw/model/model_hash.rb +9 -1
- data/lib/spiderfw/model/query.rb +50 -9
- data/lib/spiderfw/model/query_set.rb +211 -44
- data/lib/spiderfw/model/request.rb +28 -21
- data/lib/spiderfw/model/storage/base_storage.rb +125 -10
- data/lib/spiderfw/model/storage/db/db_storage.rb +7 -4
- data/lib/spiderfw/model/storage.rb +8 -1
- data/lib/spiderfw/setup/spider_setup_wizard.rb +9 -7
- data/lib/spiderfw/spider.rb +270 -43
- data/lib/spiderfw/templates/layout.rb +9 -4
- data/lib/spiderfw/templates/resources/sass.rb +3 -2
- data/lib/spiderfw/templates/template.rb +1 -0
- data/lib/spiderfw/utils/annotations.rb +3 -1
- data/lib/spiderfw/utils/logger.rb +1 -1
- data/lib/spiderfw/utils/monkey/symbol.rb +4 -2
- data/lib/spiderfw/utils/shared_store/file_shared_store.rb +2 -2
- data/lib/spiderfw/utils/thread_out.rb +3 -1
- data/public/css/error_page.css +83 -0
- data/public/js/error_page.js +5 -0
- data/spider.gemspec +4 -1
- data/templates/email/error.erb +9 -0
- metadata +28 -12
- data/apps/config_editor/widgets/edit_bool/edit_bool.rb +0 -8
- data/apps/config_editor/widgets/edit_bool/edit_bool.shtml +0 -5
data/lib/spiderfw/app.rb
CHANGED
@@ -3,249 +3,391 @@ require 'tsort'
|
|
3
3
|
|
4
4
|
module Spider
|
5
5
|
|
6
|
+
# The Spider::App module must be included in each apps' main module inside the
|
7
|
+
# apps's _init.rb file.
|
8
|
+
#
|
9
|
+
# Example: apps/my_app/_init.rb
|
10
|
+
#
|
11
|
+
# module MyApp
|
12
|
+
# include Spider::App
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# It extends the including module, defining several variables and Class methods.
|
16
|
+
# All variables are pre-set to defaults, but can be overridden by the including Module
|
17
|
+
# (see {ClassMethods}).
|
18
|
+
#
|
19
|
+
# The app's module can implement several lifecycle hooks (as Module methods):
|
20
|
+
# * app_init: called during the framework's init phase
|
21
|
+
# * app_startup: called when a server is started
|
22
|
+
# * app_shutdown: called when a server is shut down
|
6
23
|
module App
|
7
24
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
25
|
+
# Methods set on the App main module.
|
26
|
+
# All instance attributes will be set to default values, but can be overridden
|
27
|
+
# by the module by setting the corresponding instance variable.
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# module MyApp
|
31
|
+
# include Spider::App
|
32
|
+
# @controller = :MyController
|
33
|
+
# @label = 'My Application'
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
module ClassMethods
|
37
|
+
|
38
|
+
# @return [String] unique identifier for the app
|
39
|
+
attr_reader :id
|
40
|
+
# @return [String] filesystem path of the app
|
41
|
+
attr_reader :path
|
42
|
+
# @return [String] path of the 'public' folder
|
43
|
+
attr_reader :pub_path
|
44
|
+
# @return [String] path of the 'test' folder
|
45
|
+
attr_reader :test_path
|
46
|
+
# @return [String] path of the 'setup' folder
|
47
|
+
attr_reader :setup_path
|
48
|
+
# @return [String] path of the 'widgets' folder
|
49
|
+
attr_reader :widgets_path
|
50
|
+
# @return [String] path of the 'views' folder
|
51
|
+
attr_reader :views_path
|
52
|
+
# @return [String] path of the 'tags' folder
|
53
|
+
attr_reader :tags_path
|
54
|
+
# @return [String] path of the 'models' folder
|
55
|
+
attr_reader :models_path
|
56
|
+
# @return [String] name, without spaces
|
57
|
+
attr_reader :short_name
|
58
|
+
# @return [String] url from which the app will be routed
|
59
|
+
attr_reader :route_url
|
60
|
+
# @return [String] app
|
61
|
+
attr_reader :label
|
62
|
+
# @return [Gem::Version] app's version
|
63
|
+
attr_reader :version
|
64
|
+
# @return [String] prefix used to distinguish Database table
|
65
|
+
attr_accessor :short_prefix
|
66
|
+
# @return [AppSpec] the app's AppSpec
|
67
|
+
attr_reader :spec
|
68
|
+
# @return [Array] A list of directories to look for translations
|
69
|
+
attr_reader :gettext_dirs
|
70
|
+
# @return [Array] File extensions to parse for translations
|
71
|
+
attr_reader :gettext_extensions
|
72
|
+
# @return [Array] Additional GetText parasers to use
|
73
|
+
attr_reader :gettext_parsers
|
74
|
+
|
75
|
+
|
76
|
+
# Initializes missing variables to default variables.
|
77
|
+
def init
|
78
|
+
unless @path
|
79
|
+
file = caller[1].split(':')[0]
|
80
|
+
dir = File.dirname(file)
|
81
|
+
@path = dir
|
82
|
+
end
|
83
|
+
@path = File.expand_path(@path)
|
84
|
+
@short_name ||= Inflector.underscore(self.name).gsub(File::SEPARATOR, '_')
|
85
|
+
@dotted_name = Inflector.underscore(self.name).gsub(File::SEPARATOR, '.')
|
86
|
+
@pub_path ||= File.join(@path, 'public')
|
87
|
+
@test_path ||= File.join(@path, 'test')
|
88
|
+
@setup_path ||= File.join(@path, 'setup')
|
89
|
+
@models_path ||= File.join(@path, 'models')
|
90
|
+
@widgets_path ||= File.join(@path, 'widgets')
|
91
|
+
@views_path ||= File.join(@path, '/views')
|
92
|
+
@tags_path ||= File.join(@path, 'tags')
|
93
|
+
@version = Gem::Version.new(@version.to_s) if @version && !@version.is_a?(Gem::Version)
|
94
|
+
spec_path = File.join(@path, "#{@short_name}.appspec")
|
95
|
+
load_spec(spec_path) if File.exists?(spec_path)
|
96
|
+
@route_url ||= Inflector.underscore(self.name)
|
97
|
+
@label ||= @short_name.split('_').each{ |p| p[0] = p[0].chr.upcase }.join(' ')
|
98
|
+
@gettext_parsers ||= []
|
99
|
+
@gettext_dirs ||= ['lib','bin','controllers','models','views','widgets','public']
|
100
|
+
@gettext_extensions ||= ['rb','rhtml','shtml','js']
|
12
101
|
|
13
|
-
|
14
|
-
|
15
|
-
attr_reader :short_name, :route_url, :label, :version
|
16
|
-
attr_accessor :short_prefix
|
17
|
-
attr_reader :command
|
18
|
-
attr_reader :spec
|
19
|
-
attr_reader :gettext_dirs, :gettext_extensions, :gettext_parsers
|
20
|
-
|
21
|
-
def init
|
22
|
-
unless @path
|
23
|
-
file = caller[1].split(':')[0]
|
24
|
-
dir = File.dirname(file)
|
25
|
-
@path = dir
|
26
|
-
end
|
27
|
-
@path = File.expand_path(@path)
|
28
|
-
@short_name ||= Inflector.underscore(self.name).gsub(File::SEPARATOR, '_')
|
29
|
-
@dotted_name = Inflector.underscore(self.name).gsub(File::SEPARATOR, '.')
|
30
|
-
@pub_path ||= File.join(@path, 'public')
|
31
|
-
@test_path ||= File.join(@path, 'test')
|
32
|
-
@setup_path ||= File.join(@path, 'setup')
|
33
|
-
@models_path ||= File.join(@path, 'models')
|
34
|
-
@widgets_path ||= File.join(@path, 'widgets')
|
35
|
-
@views_path ||= File.join(@path, '/views')
|
36
|
-
@tags_path ||= File.join(@path, 'tags')
|
37
|
-
@version = Gem::Version.new(@version.to_s) if @version && !@version.is_a?(Gem::Version)
|
38
|
-
spec_path = File.join(@path, "#{@short_name}.appspec")
|
39
|
-
load_spec(spec_path) if File.exists?(spec_path)
|
40
|
-
@route_url ||= Inflector.underscore(self.name)
|
41
|
-
@label ||= @short_name.split('_').each{ |p| p[0] = p[0].chr.upcase }.join(' ')
|
42
|
-
@gettext_parsers ||= []
|
43
|
-
@gettext_dirs ||= ['lib','bin','controllers','models','views','widgets','public']
|
44
|
-
@gettext_extensions ||= ['rb','rhtml','shtml','js']
|
45
|
-
|
46
|
-
find_tags
|
47
|
-
end
|
102
|
+
find_tags
|
103
|
+
end
|
48
104
|
|
49
|
-
|
50
|
-
|
51
|
-
|
105
|
+
# @return [String] The apps' full_name or spec.name
|
106
|
+
def full_name
|
107
|
+
@full_name || self.spec.name
|
108
|
+
end
|
52
109
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
return nil unless Spider.site
|
75
|
-
u = "http://#{Spider.site.domain}"
|
76
|
-
u += ":#{Spider.site.port}" unless Spider.site.port == 80
|
77
|
-
u += url
|
78
|
-
u += "/"+action.to_s if action
|
79
|
-
u
|
80
|
-
end
|
81
|
-
|
82
|
-
def https_url
|
83
|
-
return nil unless Spider.site && Spider.site.ssl?
|
84
|
-
u = "https://#{Spider.site.domain}"
|
85
|
-
u += ":#{Spider.site.ssl_port}" unless Spider.site.ssl_port == 443
|
86
|
-
u += url
|
87
|
-
u
|
88
|
-
end
|
89
|
-
|
90
|
-
def http_s_url
|
91
|
-
return https_url if Spider.site && Spider.site.ssl?
|
92
|
-
return http_url
|
93
|
-
end
|
94
|
-
|
95
|
-
def pub_url
|
96
|
-
if Spider.conf.get('static_content.mode') == 'publish'
|
97
|
-
Spider::HomeController.pub_url+'/apps/'+self.short_name
|
98
|
-
else
|
99
|
-
request_url+'/public'
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def pub_url!
|
104
|
-
request_url+'/public'
|
105
|
-
end
|
106
|
-
|
107
|
-
def controller
|
108
|
-
#controllers = self.const_get(:Controllers)
|
109
|
-
if (!@controller || !const_defined?(@controller))
|
110
|
-
@controller = :AppController
|
111
|
-
return const_set(@controller, Spider::PageController.clone)
|
112
|
-
|
113
|
-
end
|
114
|
-
return const_get(@controller)
|
115
|
-
end
|
116
|
-
|
117
|
-
def models(container=nil)
|
118
|
-
container ||= self
|
119
|
-
mods = []
|
120
|
-
container.constants.each do |c|
|
121
|
-
begin
|
122
|
-
mods += get_models(container.const_get(c))
|
123
|
-
rescue LoadError
|
124
|
-
end
|
125
|
-
end
|
126
|
-
return mods
|
127
|
-
end
|
128
|
-
|
129
|
-
def get_models(m)
|
130
|
-
ms = []
|
131
|
-
if m.respond_to?(:subclass_of?) && m.subclass_of?(Spider::Model::BaseModel)
|
132
|
-
ms << m
|
133
|
-
m.constants.each do |c|
|
134
|
-
sub_mod = m.const_get(c)
|
135
|
-
next unless sub_mod.is_a?(Module)
|
136
|
-
next if !sub_mod.subclass_of?(Spider::Model::BaseModel) || sub_mod.app != self
|
137
|
-
next if sub_mod == m
|
138
|
-
ms += get_models(sub_mod)
|
139
|
-
end
|
140
|
-
elsif (m.is_a?(Module) && !m.is_a?(Class))
|
141
|
-
return models(m)
|
142
|
-
end
|
143
|
-
return ms
|
144
|
-
end
|
145
|
-
|
146
|
-
def controllers
|
147
|
-
self.constants.map{ |m| const_get(m) }.select{ |m| m.subclass_of? Spider::Controller }
|
110
|
+
# @return [String] description or spec.description or name
|
111
|
+
def description
|
112
|
+
desc = @description || self.spec.description
|
113
|
+
desc.blank? ? self.name : desc
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [String] The path used to access the application from the browser
|
117
|
+
def request_url
|
118
|
+
if u = Spider.conf.get("#{@dotted_name}.url")
|
119
|
+
return u
|
120
|
+
end
|
121
|
+
Spider::ControllerMixins::HTTPMixin.reverse_proxy_mapping('/'+@route_url)
|
122
|
+
end
|
123
|
+
alias :url :request_url
|
124
|
+
|
125
|
+
# @return [String] The full url used to access the application from the browser
|
126
|
+
def http_url(action=nil)
|
127
|
+
if u = Spider.conf.get("#{@dotted_name}.http_url")
|
128
|
+
if action
|
129
|
+
u += '/' if u[-1].chr != '/'
|
130
|
+
u += action.to_s
|
148
131
|
end
|
132
|
+
return u
|
133
|
+
end
|
134
|
+
return nil unless Spider.site
|
135
|
+
u = "http://#{Spider.site.domain}"
|
136
|
+
u += ":#{Spider.site.port}" unless Spider.site.port == 80
|
137
|
+
u += url
|
138
|
+
u += "/"+action.to_s if action
|
139
|
+
u
|
140
|
+
end
|
141
|
+
|
142
|
+
# The full url used to access the application from the browser, prefixed
|
143
|
+
# with https
|
144
|
+
# @return [String]
|
145
|
+
def https_url
|
146
|
+
return nil unless Spider.site && Spider.site.ssl?
|
147
|
+
u = "https://#{Spider.site.domain}"
|
148
|
+
u += ":#{Spider.site.ssl_port}" unless Spider.site.ssl_port == 443
|
149
|
+
u += url
|
150
|
+
u
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [String] If the site supports SSL, returns the #https_url; otherwise, the #http_url
|
154
|
+
def http_s_url
|
155
|
+
return https_url if Spider.site && Spider.site.ssl?
|
156
|
+
return http_url
|
157
|
+
end
|
158
|
+
|
159
|
+
# @return [String] The url to the app's public content. If the static_content.mode configuration
|
160
|
+
# option is set to 'publish', the app's url inside the home is returned.
|
161
|
+
def pub_url
|
162
|
+
if Spider.conf.get('static_content.mode') == 'publish'
|
163
|
+
Spider::HomeController.pub_url+'/apps/'+self.short_name
|
164
|
+
else
|
165
|
+
request_url+'/public'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# @return [String] The url to the app's public content, inside the app's folder (ignoring publishing mode)
|
170
|
+
def pub_url!
|
171
|
+
request_url+'/public'
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [Spider::Controller] The apps' main Controller.
|
175
|
+
# If setting the instance variable, use a Symbol
|
176
|
+
def controller
|
177
|
+
if (!@controller || !const_defined?(@controller))
|
178
|
+
@controller = :AppController
|
179
|
+
return const_set(@controller, Spider::PageController.clone)
|
149
180
|
|
150
|
-
|
151
|
-
|
181
|
+
end
|
182
|
+
return const_get(@controller)
|
183
|
+
end
|
184
|
+
|
185
|
+
# @return [Array] An array of all the {BaseModel} subclasses defined inside the module
|
186
|
+
def models(container=nil)
|
187
|
+
container ||= self
|
188
|
+
mods = []
|
189
|
+
container.constants.each do |c|
|
190
|
+
begin
|
191
|
+
mods += get_models(container.const_get(c))
|
192
|
+
rescue LoadError
|
152
193
|
end
|
194
|
+
end
|
195
|
+
return mods
|
196
|
+
end
|
197
|
+
|
198
|
+
# @return [Array] An array of all the {Controller} subclasses defined inside the module
|
199
|
+
def controllers
|
200
|
+
self.constants.map{ |m| const_get(m) }.select{ |m| m.subclass_of? Spider::Controller }
|
201
|
+
end
|
202
|
+
|
203
|
+
# Finds a resource (see {Spider.find_resource})
|
204
|
+
# @return [Spider::Resource]
|
205
|
+
def find_resource(type, name, cur_path=nil)
|
206
|
+
Spider.find_resource(type, name, cur_path, self)
|
207
|
+
end
|
153
208
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
self.controller.route(path, dest, options)
|
176
|
-
end
|
177
|
-
|
178
|
-
def relative_path
|
179
|
-
if Spider.paths[:apps] && @path.index(Spider.paths[:apps]) == 0
|
180
|
-
return @path[Spider.paths[:apps].length+1..-1]
|
181
|
-
else
|
182
|
-
return @path[$SPIDER_PATHS[:core_apps].length+1..-1]
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def find_tags
|
187
|
-
return unless File.directory?(@tags_path)
|
188
|
-
Dir.new(@tags_path).each do |entry|
|
189
|
-
next if entry[0].chr == '.'
|
190
|
-
next unless File.extname(entry) == '.erb'
|
191
|
-
name = File.basename(entry, '.erb')
|
192
|
-
klass = Spider::Tag.new_class(File.join(@tags_path, entry))
|
193
|
-
const_set(Spider::Inflector.camelize(name).to_sym, klass)
|
194
|
-
#Spider::Logger.debug("REGISTERED TAG #{name}, #{klass}")
|
195
|
-
register_tag(name, klass)
|
196
|
-
end
|
197
|
-
end
|
209
|
+
# Finds the path of the resource (see {Spider#find_resource})
|
210
|
+
# @return [String]
|
211
|
+
def find_resource_path(type, name, cur_path=nil)
|
212
|
+
res = Spider.find_resource(type, name, cur_path, self)
|
213
|
+
return res ? res.path : nil
|
214
|
+
end
|
215
|
+
|
216
|
+
# Calls route on the app's controller (see {Dispatcher.route}).
|
217
|
+
# @return [nil]
|
218
|
+
def route(path, dest=nil, options=nil)
|
219
|
+
self.controller.route(path, dest, options)
|
220
|
+
end
|
221
|
+
|
222
|
+
# @return [String] The app's path, relative to its container (the home or the Spider lib)
|
223
|
+
def relative_path
|
224
|
+
if Spider.paths[:apps] && @path.index(Spider.paths[:apps]) == 0
|
225
|
+
return @path[Spider.paths[:apps].length+1..-1]
|
226
|
+
else
|
227
|
+
return @path[$SPIDER_PATHS[:core_apps].length+1..-1]
|
228
|
+
end
|
229
|
+
end
|
198
230
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
@gettext_parsers ||[]
|
235
|
-
end
|
236
|
-
|
237
|
-
|
238
|
-
|
231
|
+
|
232
|
+
# Convenience method: since all classes inside the app have an #app method,
|
233
|
+
# the App itself has it too
|
234
|
+
# @return [self]
|
235
|
+
def app
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
# Require files inside the App's path
|
240
|
+
# @param [file1,file2...] files to require
|
241
|
+
# @return [nil]
|
242
|
+
def req(*list)
|
243
|
+
list.each do |file|
|
244
|
+
require File.join(@path, file)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
# Returns the currently installed version of an app
|
251
|
+
# @return [Gem::Version]
|
252
|
+
def installed_version
|
253
|
+
FileUtils.mkpath(File.dirname(installed_version_path))
|
254
|
+
return unless File.exist?(installed_version_path)
|
255
|
+
return Gem::Version.new(IO.read(installed_version_path))
|
256
|
+
end
|
257
|
+
|
258
|
+
# Sets the currently installed version of an app
|
259
|
+
# @param [String|Gem::Version] version
|
260
|
+
# @return [nil]
|
261
|
+
def installed_version=(version)
|
262
|
+
FileUtils.mkpath(File.dirname(installed_version_path))
|
263
|
+
version = Gem::Version.new(version) unless version.is_a?(Gem::Version)
|
264
|
+
File.open(installed_version_path, 'w') do |f|
|
265
|
+
f << version.to_s
|
239
266
|
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Loads the app's .spec file
|
270
|
+
# @param [String] spec_path
|
271
|
+
# @return [AppSpec]
|
272
|
+
def load_spec(spec_path=nil)
|
273
|
+
@spec = AppSpec.load(spec_path)
|
274
|
+
@spec.app_id = File.basename(spec_path, 'appsec') unless @spec.app_id
|
275
|
+
@version = @spec.version if @spec.version
|
276
|
+
@spec
|
277
|
+
end
|
278
|
+
|
279
|
+
# A list of tettext parsers to use for the app
|
280
|
+
# @return [Array]
|
281
|
+
def gettext_parsers
|
282
|
+
@gettext_parsers || []
|
283
|
+
end
|
240
284
|
|
285
|
+
# Register the pointer from a widget tag to the an object
|
286
|
+
# @param [String] tag
|
287
|
+
# @param [String] object
|
288
|
+
# @return [void]
|
289
|
+
def register_tag(tag, obj)
|
290
|
+
@tags ||= {}
|
291
|
+
@tags[tag] = obj
|
241
292
|
end
|
293
|
+
|
294
|
+
# @param [String] tag
|
295
|
+
# @return [Object] The object corresponding to a registered tag
|
296
|
+
def get_tag(tag)
|
297
|
+
@tags[tag]
|
298
|
+
end
|
299
|
+
|
300
|
+
# @param [String] tag
|
301
|
+
# @return [bool] Whether the given tag is registered
|
302
|
+
def has_tag?(tag)
|
303
|
+
return false unless @tags
|
304
|
+
@tags[tag] ? true : false
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
private
|
309
|
+
|
310
|
+
# @private
|
311
|
+
# Path to the file with the currently installed version
|
312
|
+
# @return [String]
|
313
|
+
def installed_version_path
|
314
|
+
File.join(Spider.paths[:var], 'apps', self.name, 'installed_version')
|
315
|
+
end
|
316
|
+
|
317
|
+
# Looks for erb files in the tags_path
|
318
|
+
# @return [nil]
|
319
|
+
def find_tags
|
320
|
+
return unless File.directory?(@tags_path)
|
321
|
+
Dir.new(@tags_path).each do |entry|
|
322
|
+
next if entry[0].chr == '.'
|
323
|
+
next unless File.extname(entry) == '.erb'
|
324
|
+
name = File.basename(entry, '.erb')
|
325
|
+
klass = Spider::Tag.new_class(File.join(@tags_path, entry))
|
326
|
+
const_set(Spider::Inflector.camelize(name).to_sym, klass)
|
327
|
+
#Spider::Logger.debug("REGISTERED TAG #{name}, #{klass}")
|
328
|
+
register_tag(name, klass)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Collects models ({BaseModel} subclasses) from inside the module m
|
333
|
+
# @param [Module] m
|
334
|
+
# @return [Array] the models
|
335
|
+
def get_models(m)
|
336
|
+
ms = []
|
337
|
+
if m.respond_to?(:subclass_of?) && m.subclass_of?(Spider::Model::BaseModel)
|
338
|
+
ms << m
|
339
|
+
m.constants.each do |c|
|
340
|
+
sub_mod = m.const_get(c)
|
341
|
+
next unless sub_mod.is_a?(Module)
|
342
|
+
next if !sub_mod.subclass_of?(Spider::Model::BaseModel) || sub_mod.app != self
|
343
|
+
next if sub_mod == m
|
344
|
+
ms += get_models(sub_mod)
|
345
|
+
end
|
346
|
+
elsif (m.is_a?(Module) && !m.is_a?(Class))
|
347
|
+
return models(m)
|
348
|
+
end
|
349
|
+
return ms
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.included(mod)
|
355
|
+
mod.module_eval do
|
356
|
+
|
357
|
+
include Spider::DataTypes
|
358
|
+
|
359
|
+
extend ClassMethods
|
360
|
+
end
|
361
|
+
|
242
362
|
mod.init()
|
243
363
|
Spider::add_app(mod)
|
244
364
|
end
|
245
365
|
|
366
|
+
# The AppSpec class represents an app's .spec file
|
367
|
+
# The AppSpec attributes are:
|
368
|
+
#
|
369
|
+
# * :app_id [String] sunique identifier for the app
|
370
|
+
# * :name [String] descriptive name
|
371
|
+
# * :description [String]
|
372
|
+
# * :git_repo [String] URL of git repository for the app
|
373
|
+
# * :git_repo_rw [String] URL of read/write git repository for the app
|
374
|
+
# * :authors [Array]
|
375
|
+
# * :depends [Array] Apps this app depends on
|
376
|
+
# * :depends_optional [Array] Optional dependencies
|
377
|
+
# * :load_after [Array] Apps that must be loaded before this one (if present)
|
378
|
+
# * :gems [Array] Gems this app depends on
|
379
|
+
# * :gems_optional [Array] Optional gem dependencies
|
380
|
+
# * :version [Gem::Version] Current app version
|
381
|
+
# * :app_server [String] URL for the app server of this app
|
382
|
+
# * :auto_update [TrueClass|FalseClass] true by default; set to false if this version can't be auto-updated
|
383
|
+
|
246
384
|
class AppSpec
|
385
|
+
# @private
|
247
386
|
@@attributes = []
|
248
387
|
|
388
|
+
# @private
|
389
|
+
# Helper method to define an attribute on the AppSpec class
|
390
|
+
# @return [nil]
|
249
391
|
def self.attribute(name, options={})
|
250
392
|
@@attributes << name
|
251
393
|
str = <<END_OF_EVAL
|
@@ -260,8 +402,12 @@ END_OF_EVAL
|
|
260
402
|
str += "\nalias :#{name}? :#{name}\n"
|
261
403
|
end
|
262
404
|
class_eval(str)
|
405
|
+
nil
|
263
406
|
end
|
264
407
|
|
408
|
+
# @private
|
409
|
+
# Helper method to define an Array attribute on the AppSpec class
|
410
|
+
# @return [nil]
|
265
411
|
def self.array_attribute(name, options={})
|
266
412
|
@@attributes << name
|
267
413
|
str = <<END_OF_EVAL
|
@@ -272,6 +418,7 @@ END_OF_EVAL
|
|
272
418
|
end
|
273
419
|
END_OF_EVAL
|
274
420
|
class_eval(str)
|
421
|
+
nil
|
275
422
|
end
|
276
423
|
|
277
424
|
attribute :app_id
|
@@ -283,48 +430,70 @@ END_OF_EVAL
|
|
283
430
|
array_attribute :depends
|
284
431
|
array_attribute :depends_optional
|
285
432
|
array_attribute :load_after
|
286
|
-
array_attribute :can_use
|
287
433
|
array_attribute :gems
|
288
434
|
array_attribute :gems_optional
|
289
435
|
attribute :version
|
290
436
|
attribute :app_server
|
291
437
|
attribute :auto_update, :default => true
|
292
438
|
|
439
|
+
# The git branch used for the app
|
293
440
|
attr_accessor :branch
|
294
441
|
|
442
|
+
# Sets or retrieves the AppSpec id
|
443
|
+
# @return [String]
|
295
444
|
def id(val=nil)
|
296
445
|
self.app_id(val)
|
297
446
|
end
|
298
447
|
|
448
|
+
# Sets or retrieves the AppSpec version
|
449
|
+
# @param [String] val
|
450
|
+
# @return [Gem::Version]
|
299
451
|
def version(val=nil)
|
300
452
|
@version = Gem::Version.new(val) if val
|
301
453
|
@version
|
302
454
|
end
|
303
455
|
|
456
|
+
# Sets or retrieves the first AppSpec author
|
457
|
+
# @return [String]
|
304
458
|
def author(val = nil)
|
305
459
|
@authors = [val] if val
|
306
460
|
@authors ||= []
|
307
461
|
@authors[0]
|
308
462
|
end
|
309
463
|
|
464
|
+
# Loads attributes from a .spec file
|
465
|
+
# @param [String] spec_path
|
466
|
+
# @return [self]
|
310
467
|
def load(spec_path)
|
311
468
|
self.eval(File.read(spec_path), spec_path)
|
312
469
|
self
|
313
470
|
end
|
314
471
|
|
472
|
+
# Returns a new AppSpec instance, loading from a .spec file
|
473
|
+
# @param [String] spec_path
|
474
|
+
# @return [AppSpec]
|
315
475
|
def self.load(spec_path)
|
316
476
|
self.new.load(spec_path)
|
317
477
|
end
|
318
478
|
|
479
|
+
|
480
|
+
# Evals the given code in the AppSpec's context
|
481
|
+
# @return [AppSpec]
|
319
482
|
def eval(text, path=nil)
|
320
483
|
self.instance_eval(text)
|
321
484
|
self
|
322
485
|
end
|
323
486
|
|
487
|
+
# Returns a new AppSpec instance, evaluating the given code
|
488
|
+
# @param [String] text code to evaluate
|
489
|
+
# @param [String] path path to the code
|
490
|
+
# @return [AppSpec]
|
324
491
|
def self.eval(text, path=nil)
|
325
492
|
self.new.eval(text, path)
|
326
493
|
end
|
327
494
|
|
495
|
+
# Returns all attributes as an Hash
|
496
|
+
# @return [Hash]
|
328
497
|
def to_h
|
329
498
|
h = {}
|
330
499
|
@@attributes.each do |a|
|
@@ -334,10 +503,16 @@ END_OF_EVAL
|
|
334
503
|
h
|
335
504
|
end
|
336
505
|
|
506
|
+
# Returns the Hash (as in #to_hash) as JSON
|
507
|
+
# @param [Hash] opts JSON options
|
508
|
+
# @return [String]
|
337
509
|
def to_json(opts=nil)
|
338
510
|
to_h.to_json
|
339
511
|
end
|
340
512
|
|
513
|
+
# Constructs a new AppSpec instance from an Hash of attributes
|
514
|
+
# @param [Hash] h
|
515
|
+
# @return [AppSpec]
|
341
516
|
def self.parse_hash(h)
|
342
517
|
spec = self.new
|
343
518
|
h.each do |key, value|
|
@@ -350,21 +525,28 @@ END_OF_EVAL
|
|
350
525
|
spec
|
351
526
|
end
|
352
527
|
|
528
|
+
# Returns an array of apps needed at runtime
|
529
|
+
# @return [Array]
|
353
530
|
def get_runtime_dependencies
|
354
531
|
return self.load_after unless @load_after.blank?
|
355
532
|
return self.depends + self.depends_optional
|
356
533
|
end
|
357
534
|
|
535
|
+
# Returns an Array of gem names for gems this AppSpec depends on
|
536
|
+
# @return [Array]
|
358
537
|
def gems_list
|
359
538
|
self.gems.map{ |g| g.is_a?(Array) ? g.first : g }
|
360
539
|
end
|
361
540
|
|
541
|
+
# Returns an Array of optional gem names
|
542
|
+
# @return [Array]
|
362
543
|
def gems_optional_list
|
363
544
|
self.gems_optional.map{ |g| g.is_a?(Array) ? g.first : g }
|
364
545
|
end
|
365
546
|
|
366
547
|
end
|
367
548
|
|
549
|
+
# Helper class to sort the runtime dependencies of an app using TSort.
|
368
550
|
class RuntimeSort
|
369
551
|
|
370
552
|
def initialize
|
@@ -372,6 +554,9 @@ END_OF_EVAL
|
|
372
554
|
@apps_hash = {}
|
373
555
|
end
|
374
556
|
|
557
|
+
|
558
|
+
# Adds an app to be sorted
|
559
|
+
# @param [AppSpec|String] app the app to add
|
375
560
|
def add(app)
|
376
561
|
@apps << app
|
377
562
|
if app.is_a?(AppSpec)
|
@@ -381,15 +566,22 @@ END_OF_EVAL
|
|
381
566
|
end
|
382
567
|
end
|
383
568
|
|
569
|
+
# Runs block on each dependency
|
570
|
+
# @param [Proc] block
|
384
571
|
def tsort_each_node(&block)
|
385
572
|
@apps.each(&block)
|
386
573
|
end
|
387
574
|
|
575
|
+
# Runs the given block for each dependency of node
|
576
|
+
# @param [AppSpec] node the app to get dependecies for
|
577
|
+
# @param [Proc] block
|
388
578
|
def tsort_each_child(node, &block)
|
389
579
|
return unless node.is_a?(AppSpec)
|
390
580
|
node.get_runtime_dependencies.map{ |a| @apps_hash[a] }.each(&block)
|
391
581
|
end
|
392
582
|
|
583
|
+
# Runs tsort
|
584
|
+
# @return [Array] an array of sorted App ids
|
393
585
|
def tsort
|
394
586
|
sorted = super
|
395
587
|
sorted.map{ |a| a.is_a?(AppSpec) ? a.app_id : a }
|