zena 1.0.0.beta2 → 1.0.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/History.txt +12 -0
- data/app/controllers/application_controller.rb +0 -1
- data/app/controllers/columns_controller.rb +11 -1
- data/app/controllers/nodes_controller.rb +79 -19
- data/app/controllers/versions_controller.rb +0 -2
- data/app/controllers/virtual_classes_controller.rb +19 -6
- data/app/models/column.rb +5 -1
- data/app/models/comment.rb +1 -6
- data/app/models/node.rb +21 -3
- data/app/models/role.rb +21 -0
- data/app/models/site.rb +7 -2
- data/app/models/template.rb +3 -3
- data/app/models/text_document.rb +4 -4
- data/app/models/user.rb +21 -8
- data/app/views/columns/_li.html.erb +1 -0
- data/app/views/nodes/_groups.rhtml +1 -1
- data/app/views/sites/_form.erb +3 -1
- data/app/views/sites/_li.erb +1 -0
- data/app/views/sites/index.erb +1 -1
- data/app/views/virtual_classes/_form.erb +11 -2
- data/app/views/virtual_classes/_li.erb +5 -2
- data/bin/zena +1 -1
- data/bricks/math/lib/bricks/math.rb +1 -1
- data/bricks/mongrel/README +3 -0
- data/bricks/mongrel/zena/deploy.rb +56 -0
- data/bricks/passenger/README +3 -0
- data/bricks/passenger/zena/deploy.rb +49 -0
- data/config/bricks.yml +6 -0
- data/config/deploy.rb +24 -18
- data/config/gems.yml +3 -3
- data/db/migrate/20100915062903_add_api_group_id_to_site.rb +9 -0
- data/lib/tasks/zena.rake +39 -35
- data/lib/zena.rb +5 -6
- data/lib/zena/acts/enrollable.rb +37 -6
- data/lib/zena/app.rb +4 -2
- data/lib/zena/deploy.rb +110 -150
- data/lib/zena/deploy/awstats.conf.rhtml +4 -4
- data/lib/zena/deploy/httpd.rhtml +2 -1
- data/lib/zena/deploy/stats.vhost.rhtml +7 -7
- data/lib/zena/deploy/vhost.rhtml +1 -1
- data/lib/zena/deploy/vhost_www.rhtml +4 -4
- data/lib/zena/foxy_parser.rb +6 -5
- data/lib/zena/info.rb +1 -1
- data/lib/zena/integration/test_case.rb +8 -3
- data/lib/zena/parser.rb +11 -11
- data/lib/zena/parser/zafu_tags.rb +2 -2
- data/lib/zena/remote.rb +16 -0
- data/lib/zena/remote/connection.rb +67 -0
- data/lib/zena/remote/interface.rb +405 -0
- data/lib/zena/remote/klass.rb +14 -0
- data/lib/zena/remote/mock.rb +58 -0
- data/lib/zena/remote/node.rb +76 -0
- data/lib/zena/routes.rb +2 -1
- data/lib/zena/use.rb +9 -4
- data/lib/zena/use/ajax.rb +3 -3
- data/lib/zena/use/authlogic.rb +8 -1
- data/lib/zena/use/context.rb +22 -21
- data/lib/zena/use/dates.rb +26 -3
- data/lib/zena/use/display.rb +33 -5
- data/lib/zena/use/forms.rb +90 -12
- data/lib/zena/use/fulltext.rb +1 -1
- data/lib/zena/use/i18n.rb +118 -31
- data/lib/zena/use/query_builder.rb +7 -5
- data/lib/zena/use/query_node.rb +30 -4
- data/lib/zena/use/rendering.rb +1 -1
- data/lib/zena/use/search.rb +10 -7
- data/lib/zena/use/urls.rb +3 -3
- data/lib/zena/use/zafu_attributes.rb +2 -2
- data/lib/zena/use/zafu_eval.rb +1 -1
- data/lib/zena/use/zafu_safe_definitions.rb +1 -0
- data/lib/zena/use/zafu_templates.rb +1 -1
- data/lib/zena/zafu_compiler.rb +5 -1
- data/public/javascripts/zena.js +4 -4
- data/public/stylesheets/admin.css +1 -0
- data/test/custom_queries/complex.host.yml +3 -3
- data/test/fixtures/files/translations_fr.yml +4 -1
- data/test/functional/application_controller_test.rb +2 -2
- data/test/functional/nodes_controller_test.rb +57 -5
- data/test/functional/users_controller_test.rb +10 -9
- data/test/functional/virtual_classes_controller_test.rb +48 -0
- data/test/integration/navigation_test.rb +13 -1
- data/test/integration/query_node/filters.yml +5 -0
- data/test/integration/query_node_test.rb +1 -1
- data/test/integration/zafu_compiler/ajax.yml +13 -19
- data/test/integration/zafu_compiler/basic.yml +0 -72
- data/test/integration/zafu_compiler/complex.yml +1 -1
- data/test/integration/zafu_compiler/complex_ok.yml +19 -0
- data/test/integration/zafu_compiler/dates.yml +62 -1
- data/test/integration/zafu_compiler/display.yml +4 -4
- data/test/integration/zafu_compiler/forms.yml +19 -7
- data/test/integration/zafu_compiler/i18n.yml +56 -1
- data/test/integration/zafu_compiler/later.yml +23 -1
- data/test/integration/zafu_compiler/relations.yml +1 -1
- data/test/integration/zafu_compiler/roles.yml +29 -1
- data/test/integration/zafu_compiler/safe_definitions.yml +1 -1
- data/test/integration/zafu_compiler/zafu_attributes.yml +2 -1
- data/test/integration/zafu_compiler_test.rb +5 -3
- data/test/sites/zena/columns.yml +3 -0
- data/test/sites/zena/roles.yml +0 -1
- data/test/sites/zena/sites.yml +1 -0
- data/test/sites/zena/versions.yml +2 -0
- data/test/unit/node_test.rb +27 -9
- data/test/unit/relation_proxy_test.rb +7 -4
- data/test/unit/remote_test.rb +379 -0
- data/test/unit/user_test.rb +47 -0
- data/test/unit/zena/acts/enrollable_test.rb +36 -7
- data/test/unit/zena/acts/serializable_test.rb +14 -2
- data/test/unit/zena/use/i18n_test.rb +32 -5
- data/test/unit/zena/use/query_node_test.rb +13 -1
- data/zena.gemspec +25 -11
- metadata +24 -10
@@ -48,7 +48,7 @@
|
|
48
48
|
# If there is several log files from load balancing servers :
|
49
49
|
# Example: "/pathtotools/logresolvemerge.pl *.log |"
|
50
50
|
#
|
51
|
-
LogFile="
|
51
|
+
LogFile="<%= config[:sites_root] %>/<%= config[:host] %>/log/apache2.access.log"
|
52
52
|
|
53
53
|
|
54
54
|
# Enter the log file type you want to analyze.
|
@@ -150,7 +150,7 @@ LogSeparator=" "
|
|
150
150
|
# Example: "ftp.domain.com"
|
151
151
|
# Example: "domain.com"
|
152
152
|
#
|
153
|
-
SiteDomain="<%= host %>"
|
153
|
+
SiteDomain="<%= config[:host] %>"
|
154
154
|
|
155
155
|
|
156
156
|
# Enter here all other possible domain names, addresses or virtual host
|
@@ -165,7 +165,7 @@ SiteDomain="<%= host %>"
|
|
165
165
|
# Note: You can also use @/mypath/myfile if list of aliases are in a file.
|
166
166
|
# Example: "www.myserver.com localhost 127.0.0.1 REGEX[mydomain\.(net|org)$]"
|
167
167
|
#
|
168
|
-
HostAliases="localhost 127.0.0.1 <%= host %> stats.<%= host %>"
|
168
|
+
HostAliases="localhost 127.0.0.1 <%= config[:host] %> stats.<%= config[:host] %>"
|
169
169
|
|
170
170
|
|
171
171
|
# If you want to have hosts reported by name instead of ip address, AWStats
|
@@ -200,7 +200,7 @@ DNSLookup=2
|
|
200
200
|
# Example: "C:/awstats_data_dir"
|
201
201
|
# Default: "." (means same directory as awstats.pl)
|
202
202
|
#
|
203
|
-
DirData="
|
203
|
+
DirData="<%= config[:sites_root] %>/<%= config[:host] %>/log/awstats"
|
204
204
|
|
205
205
|
|
206
206
|
# Relative or absolute web URL of your awstats cgi-bin directory.
|
data/lib/zena/deploy/httpd.rhtml
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
|
4
4
|
NameVirtualHost *
|
5
5
|
<% if config[:app_type] == :passenger %>
|
6
|
-
LoadModule upload_progress_module
|
6
|
+
LoadModule upload_progress_module /usr/lib/apache2/modules/mod_upload_progress.so
|
7
|
+
PassengerDefaultUser www-data
|
7
8
|
<% elsif config[:app_type] == :mongrel %>
|
8
9
|
<Proxy *>
|
9
10
|
Order allow,deny
|
@@ -1,19 +1,19 @@
|
|
1
|
-
# zena awstats vhost for <%= host %>
|
1
|
+
# zena awstats vhost for <%= config[:host] %>
|
2
2
|
# automatically generated file
|
3
3
|
|
4
4
|
<VirtualHost *>
|
5
|
-
ServerName stats.<%= host %>
|
5
|
+
ServerName stats.<%= config[:host] %>
|
6
6
|
|
7
7
|
DocumentRoot /usr/share/doc/awstats/examples
|
8
|
-
ErrorLog
|
9
|
-
CustomLog
|
8
|
+
ErrorLog <%= config[:sites_root] %>/<%= config[:host] %>/log/apache2.error.log
|
9
|
+
CustomLog <%= config[:sites_root] %>/<%= config[:host] %>/log/apache2.access.log combined
|
10
10
|
|
11
11
|
<location />
|
12
|
-
SetEnv AWSTATS_FORCE_CONFIG <%= host %>
|
12
|
+
SetEnv AWSTATS_FORCE_CONFIG <%= config[:host] %>
|
13
13
|
|
14
14
|
AuthType Basic
|
15
|
-
AuthName "<%= host %> stats"
|
16
|
-
AuthUserFile
|
15
|
+
AuthName "<%= config[:host] %> stats"
|
16
|
+
AuthUserFile <%= config[:sites_root] %>/<%= config[:host] %>/log/.awstatspw
|
17
17
|
Require valid-user
|
18
18
|
</location>
|
19
19
|
|
data/lib/zena/deploy/vhost.rhtml
CHANGED
@@ -66,7 +66,7 @@
|
|
66
66
|
RewriteCond %{REQUEST_FILENAME} !-f
|
67
67
|
RewriteRule ^/(.*)$ balancer://<%= config[:balancer] %>%{REQUEST_URI} [P,QSA,L]
|
68
68
|
<% elsif config[:app_type] == :passenger %>
|
69
|
-
PassengerAppRoot
|
69
|
+
PassengerAppRoot <%= config[:app_root] %>
|
70
70
|
|
71
71
|
<Location />
|
72
72
|
# enable tracking uploads in /
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# zena apache2 vhost for <%= host %>
|
1
|
+
# zena apache2 vhost for <%= config[:host] %>
|
2
2
|
# automatically generated file
|
3
3
|
|
4
4
|
<VirtualHost *>
|
5
|
-
ServerName www.<%= host %>
|
5
|
+
ServerName www.<%= config[:host] %>
|
6
6
|
|
7
7
|
RewriteEngine On
|
8
|
-
RewriteCond %{HTTP_HOST} ^www\.<%= host %>$ [NC]
|
9
|
-
RewriteRule ^(.*)$ http://<%= host %>$1 [R=301,L]
|
8
|
+
RewriteCond %{HTTP_HOST} ^www\.<%= config[:host] %>$ [NC]
|
9
|
+
RewriteRule ^(.*)$ http://<%= config[:host] %>$1 [R=301,L]
|
10
10
|
</VirtualHost>
|
data/lib/zena/foxy_parser.rb
CHANGED
@@ -80,10 +80,12 @@ module Zena
|
|
80
80
|
@options = opts
|
81
81
|
end
|
82
82
|
|
83
|
+
def sites
|
84
|
+
@sites ||= Dir["#{Zena::ROOT}/test/sites/*", "#{RAILS_ROOT}/bricks/**/sites/*"].map {|s| File.directory?(s) ? File.basename(s) : nil}.compact.uniq
|
85
|
+
end
|
86
|
+
|
83
87
|
def run
|
84
|
-
|
85
|
-
Dir.foreach(base) do |site|
|
86
|
-
next if (site =~ /^\./) || !File.directory?(File.join(base, site))
|
88
|
+
sites.each do |site|
|
87
89
|
@site = site
|
88
90
|
parse_fixtures
|
89
91
|
after_parse
|
@@ -834,8 +836,7 @@ module Zena
|
|
834
836
|
end
|
835
837
|
|
836
838
|
def run
|
837
|
-
|
838
|
-
next if site =~ /^\./ || !File.directory?(File.join("#{Zena::ROOT}/test/sites",site))
|
839
|
+
sites.each do |site|
|
839
840
|
@inserted_keys = []
|
840
841
|
out ""
|
841
842
|
out "#{site}:"
|
data/lib/zena/info.rb
CHANGED
@@ -10,7 +10,7 @@ module Zena
|
|
10
10
|
Page.logger
|
11
11
|
end
|
12
12
|
|
13
|
-
# Mock request to remote service by doing a call to the integration test.
|
13
|
+
# Mock ActiveResource request to remote service by doing a call to the integration test.
|
14
14
|
def request(method, path, *arguments)
|
15
15
|
case method
|
16
16
|
when :get, :delete, :head
|
@@ -21,14 +21,19 @@ module Zena
|
|
21
21
|
# The params here contain an xml string representing the request body.
|
22
22
|
params = arguments.first
|
23
23
|
end
|
24
|
-
|
24
|
+
test_request(method, path, params, headers)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Mock HTTParty::Request request
|
28
|
+
def test_request(method, path, params, headers, parse_response = true)
|
29
|
+
logger.info "#{method.to_s.upcase} #{site.scheme}://#{site.host}#{path} (#{headers.inspect})" if logger
|
25
30
|
result = nil
|
26
31
|
ms = Benchmark.ms do
|
27
32
|
@test.send(method, "#{site.scheme}://#{site.host}#{path}", params, headers)
|
28
33
|
result = @test.response
|
29
34
|
end
|
30
35
|
logger.info "--> %d %s (%d %.0fms)" % [result.code, result.message, result.body ? result.body.length : 0, ms] if logger
|
31
|
-
handle_response(result)
|
36
|
+
parse_response ? handle_response(result) : result
|
32
37
|
rescue Timeout::Error => e
|
33
38
|
raise TimeoutError.new(e.message)
|
34
39
|
end
|
data/lib/zena/parser.rb
CHANGED
@@ -14,10 +14,10 @@ module Zena
|
|
14
14
|
@strings = strings
|
15
15
|
end
|
16
16
|
|
17
|
-
def get_template_text(src,
|
18
|
-
|
17
|
+
def get_template_text(src, base_path)
|
18
|
+
base_path = (base_path && base_path != '') ? base_path[1..-1].split('/') : []
|
19
19
|
src = src[1..-1] if src[0..0] == '/' # just ignore the 'relative' or 'absolute' tricks.
|
20
|
-
url = (
|
20
|
+
url = (base_path + src.split('/')).join('_')
|
21
21
|
if test = @strings[url]
|
22
22
|
return [test['src'], url.split('_').join('/')]
|
23
23
|
else
|
@@ -55,16 +55,16 @@ module Zena
|
|
55
55
|
def new_with_url(url, opts={})
|
56
56
|
helper = opts[:helper] || ParserModule::DummyHelper.new
|
57
57
|
text, absolute_url = self.get_template_text(url, helper)
|
58
|
-
|
59
|
-
self.new(text, :helper=>helper, :
|
58
|
+
base_path = absolute_url ? absolute_url.split('/')[1..-2].join('/') : nil
|
59
|
+
self.new(text, :helper=>helper, :base_path=>base_path, :included_history=>[absolute_url], :root => url)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Retrieve the template text in the current folder or as an absolute path.
|
63
63
|
# This method is used when 'including' text
|
64
|
-
def get_template_text(url, helper,
|
64
|
+
def get_template_text(url, helper, base_path=nil)
|
65
65
|
|
66
|
-
if (url[0..0] != '/') &&
|
67
|
-
url = "#{
|
66
|
+
if (url[0..0] != '/') && base_path
|
67
|
+
url = "#{base_path}/#{url}"
|
68
68
|
end
|
69
69
|
|
70
70
|
res = helper.send(:get_template_text, url, nil)
|
@@ -237,7 +237,7 @@ module Zena
|
|
237
237
|
# fetch text
|
238
238
|
@options[:included_history] ||= []
|
239
239
|
|
240
|
-
included_text, absolute_url = self.class.get_template_text(@params[:template], @options[:helper], @options[:
|
240
|
+
included_text, absolute_url = self.class.get_template_text(@params[:template], @options[:helper], @options[:base_path])
|
241
241
|
|
242
242
|
if included_text
|
243
243
|
absolute_url += "::#{@params[:part].gsub('/','_')}" if @params[:part]
|
@@ -246,13 +246,13 @@ module Zena
|
|
246
246
|
included_text = "<span class='parser_error'>[include] infinity loop: #{(@options[:included_history] + [absolute_url]).join(' --> ')}</span>"
|
247
247
|
else
|
248
248
|
included_history = @options[:included_history] + [absolute_url]
|
249
|
-
|
249
|
+
base_path = absolute_url.split('/')[1..-2].join('/')
|
250
250
|
end
|
251
251
|
else
|
252
252
|
return "<span class='parser_error'>[include] template '#{url}' not found</span>"
|
253
253
|
end
|
254
254
|
|
255
|
-
res = self.class.new(included_text, :helper=>@options[:helper], :
|
255
|
+
res = self.class.new(included_text, :helper=>@options[:helper], :base_path=>base_path, :included_history=>included_history, :part => @params[:part], :root=>@options[:root]) # we set :part to avoid loop failure when doing self inclusion
|
256
256
|
|
257
257
|
if @params[:part]
|
258
258
|
if iblock = res.ids[@params[:part]]
|
@@ -125,7 +125,7 @@ module Zena
|
|
125
125
|
$&
|
126
126
|
else
|
127
127
|
quote = $1
|
128
|
-
new_src = @options[:helper].send(:template_url_for_asset, :
|
128
|
+
new_src = @options[:helper].send(:template_url_for_asset, :base_path => @options[:base_path], :src => $2)
|
129
129
|
"url(#{quote}#{new_src}#{quote})"
|
130
130
|
end
|
131
131
|
end
|
@@ -136,7 +136,7 @@ module Zena
|
|
136
136
|
|
137
137
|
src = @params[key]
|
138
138
|
if src && src[0..0] != '/' && src[0..6] != 'http://'
|
139
|
-
@params[key] = @options[:helper].send(:template_url_for_asset, :src => src, :
|
139
|
+
@params[key] = @options[:helper].send(:template_url_for_asset, :src => src, :base_path => @options[:base_path], :type => type)
|
140
140
|
end
|
141
141
|
|
142
142
|
res = "<#{@html_tag}#{params_to_html(@params)}"
|
data/lib/zena/remote.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'zena/remote/interface'
|
3
|
+
require 'zena/remote/klass'
|
4
|
+
require 'zena/remote/node'
|
5
|
+
require 'zena/remote/connection'
|
6
|
+
|
7
|
+
module Zena
|
8
|
+
module Remote
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# Create a new connection to a remote Zena application
|
12
|
+
def connect(uri, token)
|
13
|
+
Connection.connect(uri, token)
|
14
|
+
end
|
15
|
+
end # Remote
|
16
|
+
end # Zena
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'HTTParty'
|
2
|
+
|
3
|
+
module Zena
|
4
|
+
module Remote
|
5
|
+
class Connection
|
6
|
+
def initialize
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return a sub-class of Zena::Remote::Connection with the specified connection tokens built in.
|
10
|
+
# We create a new class because HTTParty works this way (class globals).
|
11
|
+
def self.connect(uri, token)
|
12
|
+
Class.new(self) do
|
13
|
+
include HTTParty
|
14
|
+
extend Zena::Remote::Interface::ConnectionMethods
|
15
|
+
|
16
|
+
class << self
|
17
|
+
alias http_delete delete
|
18
|
+
alias delete destroy
|
19
|
+
end
|
20
|
+
|
21
|
+
@found_classes = {}
|
22
|
+
@uri = uri
|
23
|
+
@message_logger = STDOUT
|
24
|
+
|
25
|
+
def self.[](class_name)
|
26
|
+
@found_classes[class_name] ||= Zena::Remote::Klass.new(self, class_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.logger
|
30
|
+
@logger ||= default_logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.log_message(msg)
|
34
|
+
logger = @message_logger || self.logger
|
35
|
+
if logger.respond_to?(:info)
|
36
|
+
logger.info "-\n"
|
37
|
+
logger.info " %-10s: %s" % ['operation', 'message']
|
38
|
+
logger.info " %-10s: %s" % ['message', msg.inspect]
|
39
|
+
else
|
40
|
+
@message_logger.send(:puts, msg)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.message_logger=(logger)
|
45
|
+
@message_logger = logger
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.default_logger
|
49
|
+
host = URI.parse(@uri =~ %r{^\w+://} ? @uri : "http://#{@uri}").host
|
50
|
+
log_path = "log/#{host}.log"
|
51
|
+
dir = File.dirname(log_path)
|
52
|
+
Dir.mkdir(dir) unless File.exist?(dir)
|
53
|
+
Logger.new(File.open(log_path, 'ab'))
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.logger=(logger)
|
57
|
+
@logger = logger
|
58
|
+
end
|
59
|
+
|
60
|
+
headers 'Accept' => 'application/xml'
|
61
|
+
headers 'HTTP_X_AUTHENTICATION_TOKEN' => token
|
62
|
+
base_uri uri
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end # Remote
|
67
|
+
end # Zena
|
@@ -0,0 +1,405 @@
|
|
1
|
+
module Zena
|
2
|
+
module Remote
|
3
|
+
module Interface
|
4
|
+
|
5
|
+
module Logger
|
6
|
+
def logger
|
7
|
+
@connection.logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def log_message(msg)
|
11
|
+
@connection.log_message(msg)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Methods to create new remote nodes.
|
16
|
+
module Create
|
17
|
+
# Used by @connection.create(...)
|
18
|
+
module ConnectionMethods
|
19
|
+
def create(attributes)
|
20
|
+
node = Zena::Remote::Node.new(self, attributes)
|
21
|
+
node.save
|
22
|
+
node
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Used by instance.save
|
27
|
+
module InstanceMethods
|
28
|
+
def post(*args)
|
29
|
+
@connection.post(*args)
|
30
|
+
end
|
31
|
+
end # InstanceMethods
|
32
|
+
|
33
|
+
# Used by connection['Post'].find(...)
|
34
|
+
module ClassMethods
|
35
|
+
def create(attributes)
|
36
|
+
node = new(attributes)
|
37
|
+
node.save
|
38
|
+
node
|
39
|
+
end
|
40
|
+
|
41
|
+
def new(attributes)
|
42
|
+
Zena::Remote::Node.new(@connection, attributes.stringify_keys.merge('class' => @name))
|
43
|
+
end
|
44
|
+
end # ClassMethods
|
45
|
+
end # Create
|
46
|
+
|
47
|
+
# Methods to retrieve remote nodes.
|
48
|
+
module Read
|
49
|
+
# Used by @connection.find(...)
|
50
|
+
module ConnectionMethods
|
51
|
+
def find_url
|
52
|
+
"/nodes/search"
|
53
|
+
end
|
54
|
+
|
55
|
+
def root
|
56
|
+
process_find(:first, 'root', {})
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Used by instance.find(...)
|
61
|
+
module InstanceMethods
|
62
|
+
def find_url
|
63
|
+
raise Exception.new("Cannot find from a new instance (no id).") unless id
|
64
|
+
"/nodes/#{id}/find"
|
65
|
+
end
|
66
|
+
|
67
|
+
def get(*args)
|
68
|
+
@connection.get(*args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Used by connection['Post'].find(...)
|
73
|
+
module ClassMethods
|
74
|
+
def find_url
|
75
|
+
"/nodes/search"
|
76
|
+
end
|
77
|
+
|
78
|
+
def get(*args)
|
79
|
+
@connection.get(*args)
|
80
|
+
end
|
81
|
+
|
82
|
+
def process_find(count, query, options = {})
|
83
|
+
if query.kind_of?(Hash)
|
84
|
+
query = query.symbolize_keys
|
85
|
+
klass = query.delete(:class) || @name
|
86
|
+
|
87
|
+
query_args = []
|
88
|
+
|
89
|
+
query.each do |key, value|
|
90
|
+
query_args << "#{key} = #{Zena::Db.quote(value)}"
|
91
|
+
end
|
92
|
+
|
93
|
+
query = "nodes where class like #{klass} and #{query_args.join(' and ')} in site"
|
94
|
+
elsif query.kind_of?(String)
|
95
|
+
# query must be a filter
|
96
|
+
query = "nodes where class like #{@name} and #{query} in site"
|
97
|
+
elsif query.kind_of?(Fixnum)
|
98
|
+
# query is an id
|
99
|
+
query = "nodes where class like #{@name} and id = #{query} in site"
|
100
|
+
else
|
101
|
+
# no filter
|
102
|
+
query = "nodes where class like #{@name} in site"
|
103
|
+
end
|
104
|
+
|
105
|
+
super(count, query, options)
|
106
|
+
end
|
107
|
+
|
108
|
+
def all(query = nil, options = {})
|
109
|
+
process_find(:all, query, options)
|
110
|
+
end
|
111
|
+
|
112
|
+
def first(query = nil, options = {})
|
113
|
+
process_find(:first, query, options)
|
114
|
+
end
|
115
|
+
|
116
|
+
def count(query = nil, options = {})
|
117
|
+
process_find(:count, query, options)
|
118
|
+
end
|
119
|
+
end # ClassMethods
|
120
|
+
|
121
|
+
# Find remote nodes with query builder or indexed search
|
122
|
+
def find(count, query = nil, options = {})
|
123
|
+
if query.nil?
|
124
|
+
query = count
|
125
|
+
count = count.kind_of?(Fixnum) ? :first : :all
|
126
|
+
end
|
127
|
+
process_find(count, query, options)
|
128
|
+
end
|
129
|
+
|
130
|
+
def search(query, options = {})
|
131
|
+
process_find(:all, {:q => query}, options)
|
132
|
+
end
|
133
|
+
|
134
|
+
def all(query, options = {})
|
135
|
+
process_find(:all, query, options)
|
136
|
+
end
|
137
|
+
|
138
|
+
def first(query, options = {})
|
139
|
+
process_find(:first, query, options)
|
140
|
+
end
|
141
|
+
|
142
|
+
def count(query, options = {})
|
143
|
+
process_find(:count, query, options)
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
private
|
148
|
+
def process_find(count, query, options)
|
149
|
+
if query.kind_of?(String)
|
150
|
+
# Consider string as pseudo sql
|
151
|
+
result = get(find_url, :query => options.merge(:qb => query, :_find => count))
|
152
|
+
|
153
|
+
elsif query.kind_of?(Fixnum)
|
154
|
+
# Find by id (== zip)
|
155
|
+
result = get("/nodes/#{query}")
|
156
|
+
|
157
|
+
if node = result['node']
|
158
|
+
result = {'nodes' => [node]}
|
159
|
+
end
|
160
|
+
|
161
|
+
else
|
162
|
+
# Find from indices title = ..., etc
|
163
|
+
result = get(find_url, :query => query.merge(options).merge(:_find => count))
|
164
|
+
end
|
165
|
+
|
166
|
+
if errors = result['errors']
|
167
|
+
errors.each do |error|
|
168
|
+
log_message error['message']
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
case count
|
173
|
+
when :first
|
174
|
+
if nodes = result['nodes']
|
175
|
+
if found_first = nodes.first
|
176
|
+
return build_record(found_first)
|
177
|
+
else
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
else
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
when :all
|
184
|
+
if nodes = result['nodes']
|
185
|
+
return nodes.map do |hash|
|
186
|
+
build_record(hash)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
[]
|
190
|
+
end
|
191
|
+
when :count
|
192
|
+
if count = result['count']
|
193
|
+
return count
|
194
|
+
else
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
else
|
198
|
+
raise Exception.new("Invalid count should be :all, :first or :count (found #{count.inspect})")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def build_record(hash)
|
203
|
+
Zena::Remote::Node.new(self, hash)
|
204
|
+
end
|
205
|
+
end # Read
|
206
|
+
|
207
|
+
# Methods to update a remote node.
|
208
|
+
module Update
|
209
|
+
# Used to mass update
|
210
|
+
module ConnectionMethods
|
211
|
+
def update(query, attributes)
|
212
|
+
if nodes = all(query)
|
213
|
+
# TODO: ask ?
|
214
|
+
logger.info "-\n"
|
215
|
+
logger.info " %-10s: %s" % ['operation', 'mass update']
|
216
|
+
logger.info " %-10s: %s" % ['timestamp', Time.now]
|
217
|
+
logger.info " %-10s: %s" % ['query', query.inspect]
|
218
|
+
logger.info " %-10s: %s" % ['count', nodes.size]
|
219
|
+
logger.info " change:"
|
220
|
+
attributes.each do |key, value|
|
221
|
+
logger.info " #{key}: #{value.inspect}"
|
222
|
+
end
|
223
|
+
nodes.each do |node|
|
224
|
+
if node.update_attributes(attributes)
|
225
|
+
else
|
226
|
+
log_message "Could not update node #{node.id} (#{node.title}): #{node.errors.inspect}"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
nodes
|
230
|
+
else
|
231
|
+
nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Used by instance.find(...)
|
237
|
+
module InstanceMethods
|
238
|
+
def update_url
|
239
|
+
node_url
|
240
|
+
end
|
241
|
+
|
242
|
+
def node_url
|
243
|
+
"/nodes/#{id}"
|
244
|
+
end
|
245
|
+
|
246
|
+
def create_url
|
247
|
+
"/nodes"
|
248
|
+
end
|
249
|
+
|
250
|
+
def put(*args)
|
251
|
+
@connection.put(*args)
|
252
|
+
end
|
253
|
+
|
254
|
+
def update_attributes(new_attributes)
|
255
|
+
saved_attributes = @attributes.dup
|
256
|
+
self.attributes = new_attributes
|
257
|
+
if save
|
258
|
+
logger.info "-\n"
|
259
|
+
logger.info " %-10s: %s" % ['operation', 'update']
|
260
|
+
logger.info " %-10s: %s" % ['timestamp', Time.now]
|
261
|
+
logger.info " %-10s: %i" % ['node_id', id]
|
262
|
+
logger.info " changes:"
|
263
|
+
@attributes.keys.each do |key|
|
264
|
+
next if saved_attributes[key] == @attributes[key]
|
265
|
+
logger.info " #{key}:"
|
266
|
+
logger.info " old: #{saved_attributes[key].inspect}"
|
267
|
+
logger.info " new: #{@attributes[key].inspect}"
|
268
|
+
end
|
269
|
+
else
|
270
|
+
false
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def save
|
275
|
+
if new_record?
|
276
|
+
result = post(create_url, :body => {:node => @attributes})
|
277
|
+
else
|
278
|
+
result = put(update_url, :body => {:node => @attributes})
|
279
|
+
end
|
280
|
+
|
281
|
+
if result.response.code =~ /^20\d$/
|
282
|
+
if node = result['node']
|
283
|
+
@attributes = node
|
284
|
+
node
|
285
|
+
elsif errors = result['errors']
|
286
|
+
@errors = errors
|
287
|
+
log_message errors
|
288
|
+
false
|
289
|
+
else
|
290
|
+
log_message "Could not save:"
|
291
|
+
log_message result.inspect
|
292
|
+
false
|
293
|
+
end
|
294
|
+
elsif errors = result['errors']
|
295
|
+
log_message "Could not save:"
|
296
|
+
log_message errors
|
297
|
+
false
|
298
|
+
else
|
299
|
+
log_message "Could not save:"
|
300
|
+
log_message result.inspect
|
301
|
+
false
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def new_record?
|
306
|
+
id.nil?
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end # Update
|
310
|
+
|
311
|
+
# Methods to delete a remote node.
|
312
|
+
module Delete
|
313
|
+
module ConnectionMethods
|
314
|
+
def destroy(query)
|
315
|
+
if nodes = all(query)
|
316
|
+
# TODO: ask ?
|
317
|
+
logger.info "-\n"
|
318
|
+
logger.info " %-10s: %s" % ['operation', 'mass destroy']
|
319
|
+
logger.info " %-10s: %s" % ['timestamp', Time.now]
|
320
|
+
logger.info " %-10s: %s" % ['query', query.inspect]
|
321
|
+
logger.info " %-10s: %s" % ['count', nodes.size]
|
322
|
+
nodes.each do |node|
|
323
|
+
if node.destroy
|
324
|
+
else
|
325
|
+
log_message "Could not destroy node #{node.id} (#{node.title}): #{node.errors.inspect}"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
nodes
|
329
|
+
else
|
330
|
+
nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end # ConnectionMethods
|
334
|
+
|
335
|
+
module InstanceMethods
|
336
|
+
def destroy_url
|
337
|
+
node_url
|
338
|
+
end
|
339
|
+
|
340
|
+
def destroy
|
341
|
+
if id.nil?
|
342
|
+
@errors = ["cannot destroy inexistant remote node"]
|
343
|
+
return false
|
344
|
+
end
|
345
|
+
@previous_id = id
|
346
|
+
result = @connection.http_delete(destroy_url)
|
347
|
+
if result.code == 200
|
348
|
+
logger.info " %-10s: %s" % ['operation', 'destroy']
|
349
|
+
logger.info " %-10s: %s" % ['timestamp', Time.now]
|
350
|
+
logger.info " %-10s: %s" % ['node_id', id]
|
351
|
+
logger.info " attributes:"
|
352
|
+
@attributes.keys.each do |key|
|
353
|
+
logger.info " #{key}: #{@attributes[key].inspect}"
|
354
|
+
end
|
355
|
+
true
|
356
|
+
elsif errors = result['errors']
|
357
|
+
@errors = errors
|
358
|
+
false
|
359
|
+
else
|
360
|
+
log_message "Could not destroy.. error:"
|
361
|
+
log_message result.inspect
|
362
|
+
false
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end # InstanceMethods
|
366
|
+
end # Delete
|
367
|
+
|
368
|
+
# Extends the Connection class
|
369
|
+
module ConnectionMethods
|
370
|
+
include Create::ConnectionMethods
|
371
|
+
|
372
|
+
include Read
|
373
|
+
include Read::ConnectionMethods
|
374
|
+
|
375
|
+
include Update::ConnectionMethods
|
376
|
+
|
377
|
+
include Delete::ConnectionMethods
|
378
|
+
end
|
379
|
+
|
380
|
+
# Included in the Remote::Klass class
|
381
|
+
module ClassMethods
|
382
|
+
include Logger
|
383
|
+
|
384
|
+
include Create::ClassMethods
|
385
|
+
|
386
|
+
include Read
|
387
|
+
include Read::ClassMethods
|
388
|
+
end
|
389
|
+
|
390
|
+
# Included in the Remote::Node class
|
391
|
+
module InstanceMethods
|
392
|
+
include Logger
|
393
|
+
|
394
|
+
include Create::InstanceMethods
|
395
|
+
|
396
|
+
include Read
|
397
|
+
include Read::InstanceMethods
|
398
|
+
|
399
|
+
include Update::InstanceMethods
|
400
|
+
|
401
|
+
include Delete::InstanceMethods
|
402
|
+
end
|
403
|
+
end # Interface
|
404
|
+
end # Remote
|
405
|
+
end # Zena
|