marley 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -8
- data/lib/marley.rb +27 -131
- data/lib/marley/controllers.rb +12 -7
- data/lib/marley/core_ext.rb +8 -0
- data/lib/marley/errors.rb +51 -0
- data/lib/marley/joint.rb +14 -19
- data/lib/marley/joints/forum.rb +88 -0
- data/lib/marley/joints/messages.rb +86 -0
- data/lib/marley/joints/section.rb +63 -0
- data/lib/marley/joints/tags.rb +99 -0
- data/lib/marley/joints/user.rb +120 -0
- data/lib/marley/plugin.rb +39 -0
- data/lib/marley/plugins/orm_rest_convenience.rb +92 -0
- data/lib/marley/plugins/rest_convenience.rb +28 -0
- data/lib/marley/reggae.rb +5 -2
- data/lib/marley/resources.rb +10 -0
- data/lib/marley/router.rb +61 -0
- data/lib/marley/test_helpers.rb +12 -1
- data/lib/marley/utils.rb +57 -25
- data/rdoc/example_joint.rb +33 -0
- data/rdoc/example_plugin.rb +20 -0
- data/rdoc/forum_joint.rb +50 -0
- data/rdoc/forum_joint.rdoc +19 -0
- data/rdoc/hello.rb +14 -0
- data/rdoc/hello.rdoc +14 -0
- data/rdoc/joints.rdoc +69 -0
- data/rdoc/messages_joint.rb +34 -0
- data/rdoc/messages_joint.rdoc +18 -0
- data/rdoc/messages_joint/private_messages.rdoc +59 -0
- data/rdoc/messages_joint/public_messages.rdoc +65 -0
- data/rdoc/orm_rest_convenience_plugin.rb +28 -0
- data/rdoc/orm_rest_convenience_plugin.rdoc +99 -0
- data/rdoc/plugins.rdoc +35 -0
- data/rdoc/reggae.rb +3 -0
- data/rdoc/reggae.rdoc +11 -0
- data/rdoc/reggae/generate.rdoc +13 -0
- data/rdoc/reggae/parse.rdoc +44 -0
- data/rdoc/section_joint.rb +13 -0
- data/rdoc/section_joint.rdoc +19 -0
- data/rdoc/tags_joint.rb +16 -0
- data/rdoc/tags_joint.rdoc +22 -0
- data/rdoc/tags_joint/announcements.rdoc +64 -0
- data/rdoc/tags_joint/secrets.rdoc +65 -0
- data/rdoc/user_joint.rb +62 -0
- data/rdoc/user_joint.rdoc +13 -0
- data/rdoc/user_joint/exiting_users.rdoc +120 -0
- data/rdoc/user_joint/no_auth_provided.rdoc +34 -0
- data/reggae.ebnf +1 -1
- metadata +45 -37
- data/Favicon.ico +0 -0
- data/Rakefile +0 -14
- data/TODO +0 -19
- data/examples/blog.rb +0 -26
- data/examples/empty.sqlite3 +0 -0
- data/examples/forum.css +0 -3
- data/examples/forum.js +0 -23
- data/examples/forum.rb +0 -20
- data/examples/forum.sql +0 -42
- data/examples/forum.sqlite3 +0 -0
- data/examples/forum_test.sqlite3 +0 -0
- data/examples/run.sh +0 -14
- data/lib/client/jamaica.css +0 -270
- data/lib/client/jamaica.js +0 -353
- data/lib/client/jamaica.rb +0 -38
- data/lib/client/jquery-1.6.2.js +0 -8981
- data/lib/client/jquery.form.js +0 -814
- data/lib/joints/basic_menu_system.rb +0 -41
- data/lib/joints/basic_messaging.rb +0 -88
- data/lib/joints/basic_user.rb +0 -51
- data/lib/joints/tagged_messaging.rb +0 -122
- data/lib/joints/tagging.rb +0 -56
- data/lib/sequel/plugins/rest_auth.rb +0 -53
- data/lib/sequel/plugins/rest_convenience.rb +0 -87
- data/marley-0.4.0.gem +0 -0
- data/marley.gemspec +0 -17
- data/test/empty.sqlite3 +0 -0
- data/test/menu_tests.rb +0 -9
- data/test/tagged_messaging_tests.rb +0 -289
- data/test/test.sqlite3 +0 -0
- data/test/test_include.rb +0 -16
- data/test/user_tests.rb +0 -72
data/README.rdoc
CHANGED
@@ -4,12 +4,9 @@ Marley is a framework for quickly building RESTful web services and applications
|
|
4
4
|
|
5
5
|
* A simple Rack application that acts as request parser/router.
|
6
6
|
* A default controller for ORM model classes.
|
7
|
-
*
|
8
|
-
* RestConvenience - Connects the class to a default controller.
|
9
|
-
* RestAuthorization - Adds default authorization methods to a model class and its instances.
|
7
|
+
* A plugin system similar to Sequel's.
|
10
8
|
* Marley Joints - A framework for creating reusable Marley resource sets.
|
11
9
|
* Reggae - A JSON data format
|
12
|
-
* Jamaica - A minimal JS/jQuery client which renders Reggae resources as browser DOM objects.
|
13
10
|
|
14
11
|
The point of the whole thing is to minimize the amount of non-model (non-datacentric) code that needs to be written to implement a web service/application.
|
15
12
|
|
@@ -34,9 +31,7 @@ if the resource has a REST method corresponding the the request verb, the parser
|
|
34
31
|
|
35
32
|
The parser also traps various errors and returns the appropriate error message to the client.
|
36
33
|
|
37
|
-
|
38
|
-
|
39
|
-
Marley activates both of them for Sequel::Model. This will soon change to an option.
|
34
|
+
:include: rdoc/plugins.rdoc
|
40
35
|
|
41
36
|
==The Default Model Controller
|
42
37
|
|
@@ -49,5 +44,5 @@ One of the things that the RestConvenience Sequel plugin does is add a #controll
|
|
49
44
|
|
50
45
|
==Jamaica
|
51
46
|
|
52
|
-
The default Marley client is "Jamaica", which consists of JS/CSS for browsers. It
|
47
|
+
The default Marley client is "Jamaica", which consists of JS/CSS for browsers. It has now been moved to a separate repository at https://github.com/herbdaily/jamaica
|
53
48
|
|
data/lib/marley.rb
CHANGED
@@ -1,37 +1,32 @@
|
|
1
|
-
#!/usr/bin/ruby
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
require 'json/ext'
|
3
|
-
require 'json/add/core'
|
4
|
-
require 'marley/reggae'
|
5
|
-
require 'marley/utils'
|
6
3
|
require 'rack'
|
7
|
-
require 'rack/auth/basic'
|
8
4
|
require 'rack/builder'
|
5
|
+
require 'logger'
|
9
6
|
require 'sequel'
|
10
|
-
require '
|
11
|
-
require '
|
7
|
+
require 'marley/core_ext'
|
8
|
+
require 'marley/utils'
|
9
|
+
require 'marley/reggae'
|
10
|
+
require 'marley/errors'
|
11
|
+
require 'marley/router'
|
12
|
+
require 'marley/resources'
|
12
13
|
require 'marley/controllers'
|
13
|
-
require '
|
14
|
+
require 'marley/joint'
|
15
|
+
require 'marley/plugin'
|
16
|
+
require 'client/jamaica' #should prob be ditched
|
14
17
|
Sequel.extension :inflector
|
15
18
|
|
16
|
-
Sequel::Model.plugin :rest_convenience
|
17
|
-
Sequel::Model.plugin :rest_authorization
|
18
|
-
Sequel::Model.plugin :validation_helpers
|
19
|
-
Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS.merge!(:presence => {:message => 'is required'})
|
20
|
-
Sequel::Model.plugin :timestamps, :create => :date_created, :update => :date_updated
|
21
|
-
|
22
19
|
log_fn='log/marley.log'
|
23
20
|
$log=Logger.new(File.exists?(log_fn) ? log_fn : $stdout)
|
24
21
|
|
25
22
|
module Marley
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
require 'marley/joint' #this needs to happen after Marley::Resources is defined
|
23
|
+
DEFAULT_OPTS={
|
24
|
+
:app_name => 'Application',
|
25
|
+
:port => 1620,
|
26
|
+
:default_resource => 'MainMenu',
|
27
|
+
:server => 'thin',
|
28
|
+
:client => Marley::Client.new}
|
29
|
+
RESP_CODES={'get' => 200,'post' => 201,'put' => 203,'delete' => 204}
|
35
30
|
|
36
31
|
def self.config(opts=nil)
|
37
32
|
@marley_opts||=DEFAULT_OPTS
|
@@ -40,123 +35,24 @@ module Marley
|
|
40
35
|
@marley_opts
|
41
36
|
end
|
42
37
|
|
38
|
+
def self.plugin(plugin_name, *opts)
|
39
|
+
Plugins.const_get(plugin_name.to_s.camelize).new(*opts)
|
40
|
+
end
|
43
41
|
def self.joint(joint_name, *opts)
|
44
|
-
|
45
|
-
joint_d=JOINT_DIRS.find {|d| File.exists?("#{d}/#{joint_name}.rb") }
|
46
|
-
require "#{joint_d}/#{joint_name}"
|
47
|
-
@marley_opts && @marley_opts[:client] && @marley_opts[:client].joint(joint_d,joint_name)
|
48
|
-
end
|
49
|
-
Marley::Joints.const_get(joint_name.camelize).new(*opts).smoke
|
42
|
+
Joints.const_get(joint_name.to_s.camelize).new(*opts).smoke
|
50
43
|
end
|
51
44
|
|
52
45
|
def self.run(opts={})
|
53
|
-
|
54
|
-
marley_opts=@marley_opts.merge!(opts)
|
46
|
+
marley_opts=self.config(opts)
|
55
47
|
Rack::Handler.get(marley_opts[:server]).run(Rack::Builder.new {
|
56
48
|
use Rack::Reloader,0
|
57
49
|
use Rack::Static, :urls => [opts[:image_path]] if opts[:image_path]
|
58
50
|
run(Marley::Router.new(marley_opts))
|
59
51
|
}.to_app,{:Port => @marley_opts[:port]})
|
60
52
|
end
|
61
|
-
class Router
|
62
|
-
def initialize(opts={},app=nil)
|
63
|
-
@opts=DEFAULT_OPTS.merge(opts)
|
64
|
-
end
|
65
|
-
def call(env)
|
66
|
-
request= Rack::Request.new(env)
|
67
|
-
@auth = Rack::Auth::Basic::Request.new(env)
|
68
|
-
$request={:request => request,:opts => @opts}
|
69
|
-
$request[:get_params]=Marley::Utils.hash_keys_to_syms(request.GET)
|
70
|
-
$request[:post_params]=Marley::Utils.hash_keys_to_syms(request.POST)
|
71
|
-
$request[:content_type]=request.xhr? ? 'application/json' : env['HTTP_ACCEPT'].to_s.sub(/,.*/,'')
|
72
|
-
$request[:content_type]='text/html' unless $request[:content_type] > ''
|
73
|
-
$request[:content_type]='application/json' if env['rack.test']==true #there has to be a better way to do this...
|
74
|
-
if @opts[:http_auth]
|
75
|
-
if (@auth.provided? && @auth.basic? && @auth.credentials)
|
76
|
-
$request[:user]=Resources.const_get(@opts[:auth_class]).authenticate(@auth.credentials)
|
77
|
-
raise AuthenticationError unless $request[:user]
|
78
|
-
else
|
79
|
-
$request[:user]=Resources.const_get(@opts[:default_user_class]).new
|
80
|
-
end
|
81
|
-
end
|
82
|
-
$request[:path]=request.path.sub(/\/\/+/,'/').split('/')[1..-1]
|
83
|
-
verb=request.request_method.downcase
|
84
|
-
verb=$request[:post_params].delete(:_method).match(/^(put|delete)$/i)[1] rescue verb
|
85
|
-
$request[:verb]="rest_#{verb}"
|
86
|
-
rn=$request[:path] ? $request[:path][0].camelize : @opts[:default_resource]
|
87
|
-
raise RoutingError unless Resources.constants.include?(rn)
|
88
|
-
@resource=Resources.const_get(rn)
|
89
|
-
raise AuthenticationError if @opts[:http_auth] && @resource.respond_to?('requires_user?') && @resource.requires_user? && $request[:user].new?
|
90
|
-
@controller=nil
|
91
|
-
@controller=@resource.controller if @resource.respond_to?(:controller)
|
92
|
-
@controller=@resource if @resource.respond_to?($request[:verb])
|
93
|
-
raise RoutingError unless @controller
|
94
|
-
json=@controller.send($request[:verb]).to_json
|
95
|
-
html=@opts[:client] ? @opts[:client].to_s(json) : json
|
96
|
-
resp_code=RESP_CODES[verb]
|
97
|
-
headers||={'Content-Type' => "#{$request[:content_type]}; charset=utf-8"}
|
98
|
-
[resp_code,headers,$request[:content_type].match(/json/) ? json : html]
|
99
|
-
rescue Sequel::ValidationFailed
|
100
|
-
ValidationError.new($!.errors).to_a
|
101
|
-
rescue
|
102
|
-
if $!.class.superclass==MarleyError
|
103
|
-
$!.to_a
|
104
|
-
else
|
105
|
-
p $!,$!.backtrace
|
106
|
-
end
|
107
|
-
ensure
|
108
|
-
$log.info $request.merge({:request => nil,:user => $request[:user] ? $request[:user].name : nil})
|
109
|
-
end
|
110
|
-
end
|
111
|
-
class MarleyError < StandardError
|
112
|
-
class << self
|
113
|
-
attr_accessor :resp_code,:headers,:description,:details
|
114
|
-
end
|
115
|
-
@resp_code=500
|
116
|
-
def initialize
|
117
|
-
self.class.details=self.backtrace
|
118
|
-
end
|
119
|
-
def log_error
|
120
|
-
$log.fatal("#$!.message}\n#{$!.backtrace}")
|
121
|
-
end
|
122
|
-
def to_a
|
123
|
-
log_error
|
124
|
-
json=[:error,{:error_type => self.class.name.underscore.sub(/_error$/,'').sub(/^marley\//,''),:description => self.class.description, :error_details => self.class.details}].to_json
|
125
|
-
self.class.headers||={'Content-Type' => "#{$request[:content_type]}; charset=utf-8"}
|
126
|
-
[self.class.resp_code,self.class.headers,json]
|
127
|
-
end
|
128
|
-
end
|
129
|
-
class ValidationError < MarleyError
|
130
|
-
@resp_code=400
|
131
|
-
def initialize(errors)
|
132
|
-
self.class.details=errors
|
133
|
-
end
|
134
|
-
def log_error
|
135
|
-
$log.error(self.class.details)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
class AuthenticationError < MarleyError
|
139
|
-
@resp_code=401
|
140
|
-
@headers={'WWW-Authenticate' => %(Basic realm="Application")}
|
141
|
-
def log_error
|
142
|
-
$log.error("Authentication failed for #{@auth.credentials[0]}") if (@auth && @auth.provided? && @auth.basic? && @auth.credentials)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
class AuthorizationError < MarleyError
|
146
|
-
@resp_code=403
|
147
|
-
@description='You are not authorized for this operation'
|
148
|
-
def log_error
|
149
|
-
$log.error("Authorizationt Error:#{self.message}")
|
150
|
-
end
|
151
|
-
end
|
152
|
-
class RoutingError < MarleyError
|
153
|
-
@resp_code=404
|
154
|
-
@description='Not Found'
|
155
|
-
def log_error
|
156
|
-
$log.fatal("path:#{$request[:path]}\n msg:#{$!.message}\n backtrace:#{$!.backtrace}")
|
157
|
-
end
|
158
|
-
end
|
159
53
|
end
|
160
|
-
|
161
|
-
|
54
|
+
MR=Marley::Resources
|
55
|
+
MJ=Marley::Joints
|
56
|
+
MP=Marley::Plugins
|
57
|
+
MU=Marley::Utils
|
162
58
|
at_exit {Marley.run if ARGV[0]=='run'}
|
data/lib/marley/controllers.rb
CHANGED
@@ -5,6 +5,7 @@ module Marley
|
|
5
5
|
@model=model
|
6
6
|
if $request[:path][1].to_s.match(/^\d+$/) #references a specific instance by ID
|
7
7
|
@instance=@model[$request[:path][1].to_i]
|
8
|
+
raise RoutingError unless @instance
|
8
9
|
@method_name=$request[:path][2]
|
9
10
|
if @method_name
|
10
11
|
raise RoutingError unless @instance.respond_to?(@method_name)
|
@@ -35,24 +36,28 @@ module Marley
|
|
35
36
|
def rest_post
|
36
37
|
if @instance
|
37
38
|
raise RoutingError unless @method
|
38
|
-
params=$request[:post_params][@
|
39
|
+
params=$request[:post_params][@method_name.to_sym] || $request[:post_params][@model.resource_name.to_sym][@method_name.to_sym]
|
39
40
|
raise ValidationFailed unless params
|
40
41
|
params=[params] unless params.class==Array
|
41
42
|
params.map do |param|
|
42
43
|
@instance.send("add_#{@method_name}",param)
|
43
44
|
end
|
44
45
|
else
|
45
|
-
|
46
|
-
@instance.
|
46
|
+
params=($request[:post_params][@model.resource_name.to_sym]||{})
|
47
|
+
@instance=@model.new(params.reject {|k,v| v.nil?}) #reject nils to work around sequel validation flaw
|
48
|
+
raise AuthorizationError if params.keys.find {|k| ! @instance.write_cols.include?(k) }
|
49
|
+
params.keys.each {|k| @instance.send("#{k.to_s}=",params[k]) if k.to_s.match(/^_/)}
|
50
|
+
@instance.save
|
47
51
|
@instance.respond_to?('create_msg') ? @instance.create_msg : @instance
|
48
52
|
end
|
49
53
|
end
|
50
54
|
def rest_put
|
51
55
|
raise RoutingError unless @instance
|
52
|
-
(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
params=($request[:post_params][@model.resource_name.to_sym]||{})
|
57
|
+
raise AuthorizationError if params.keys.find {|k| ! @instance.write_cols.include?(k) }
|
58
|
+
params.keys.each {|k| @instance.send("#{k.to_s}=",params[k]) if k.to_s.match(/^_/)}
|
59
|
+
@instance.modified!
|
60
|
+
@instance.update_only(params,@instance.write_cols)
|
56
61
|
end
|
57
62
|
def rest_delete
|
58
63
|
raise RoutingError unless @instance
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Marley
|
2
|
+
class MarleyError < StandardError
|
3
|
+
class << self
|
4
|
+
attr_accessor :resp_code,:headers,:description,:details
|
5
|
+
end
|
6
|
+
@resp_code=500
|
7
|
+
def initialize
|
8
|
+
self.class.details=self.backtrace
|
9
|
+
end
|
10
|
+
def log_error
|
11
|
+
$log.fatal("#$!.message}\n#{$!.backtrace}")
|
12
|
+
end
|
13
|
+
def to_a
|
14
|
+
log_error
|
15
|
+
json=[:error,{:error_type => self.class.name.underscore.sub(/_error$/,'').sub(/^marley\//,''),:description => self.class.description, :error_details => self.class.details}].to_json
|
16
|
+
self.class.headers||={'Content-Type' => "#{$request[:content_type]}; charset=utf-8"}
|
17
|
+
[self.class.resp_code,self.class.headers,json]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
class ValidationError < MarleyError
|
21
|
+
@resp_code=400
|
22
|
+
def initialize(errors)
|
23
|
+
self.class.details=errors
|
24
|
+
end
|
25
|
+
def log_error
|
26
|
+
$log.error(self.class.details)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class AuthenticationError < MarleyError
|
30
|
+
@resp_code=401
|
31
|
+
@headers={'WWW-Authenticate' => %(Basic realm="Application")}
|
32
|
+
def log_error
|
33
|
+
$log.error("Authentication failed for #{@auth.credentials[0]}") if (@auth && @auth.provided? && @auth.basic? && @auth.credentials)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class AuthorizationError < MarleyError
|
37
|
+
@resp_code=403
|
38
|
+
@description='You are not authorized for this operation'
|
39
|
+
def log_error
|
40
|
+
$log.error("Authorizationt Error:#{self.message}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class RoutingError < MarleyError
|
44
|
+
@resp_code=404
|
45
|
+
@description='Not Found'
|
46
|
+
def log_error
|
47
|
+
$log.fatal("path:#{$request[:path]}\n msg:#{$!.message}\n backtrace:#{$!.backtrace}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/marley/joint.rb
CHANGED
@@ -1,35 +1,30 @@
|
|
1
1
|
module Marley
|
2
2
|
module Joints
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# - InstanceMethods - Modules within this module will append their features to any constant in Marley::Resources with the same name.
|
3
|
+
Gem.find_files("lib/marley/joints/*.rb").each do |f|
|
4
|
+
self.autoload(File.basename(f,'.rb').camelize, f)
|
5
|
+
end
|
7
6
|
class Joint
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
MODS.map {|mod_name| self.const_get(mod_name) }
|
12
|
-
end
|
7
|
+
extend Marley::Utils::ClassAttrs
|
8
|
+
class_attr(:default_opts,{})
|
9
|
+
attr_accessor :opts
|
13
10
|
def initialize(opts={})
|
14
11
|
config(opts)
|
15
12
|
end
|
16
13
|
def smoke
|
17
|
-
|
18
|
-
|
14
|
+
@opts[:required_joints].to_a.each do |j|
|
15
|
+
Marley.joint(j)
|
19
16
|
end
|
20
|
-
self.class
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
resource=MR.const_get(resource_name)
|
25
|
-
plugin.constants.include?('ClassMethods') && resource.extend(plugin.const_get('ClassMethods'))
|
26
|
-
plugin.constants.include?('InstanceMethods') && plugin.const_get('InstanceMethods').send(:append_features,resource)
|
17
|
+
resources=self.class::Resources
|
18
|
+
resources.constants.each do |resource_name|
|
19
|
+
@opts[:plugins].to_a.each do |plugin_name|
|
20
|
+
Marley.plugin(plugin_name).apply(resources.const_get(resource_name))
|
27
21
|
end
|
22
|
+
MR.const_set(resource_name, resources.const_get(resource_name)) unless (@opts[:resources] && ! @opts[:resources].include?(resource_name) )
|
28
23
|
end
|
29
24
|
self
|
30
25
|
end
|
31
26
|
def config(opts)
|
32
|
-
@opts=(@opts || {}).merge(opts)
|
27
|
+
@opts=(@opts || self.class.default_opts || {}).merge(opts)
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
module Marley
|
3
|
+
module Plugins
|
4
|
+
class MessageThreading < Plugin
|
5
|
+
module ClassMethods
|
6
|
+
def topics(params=nil)
|
7
|
+
filters=[]
|
8
|
+
if params && params[:tags]
|
9
|
+
filters << {:id => MR::Tag.join(:messages_tags, :tag_id => :id).select(:message_id).filter(:tag => params[:tags])}
|
10
|
+
end
|
11
|
+
filters.inject(self.dataset.filter(:parent_id => nil)) {|ds,f| ds.filter(f)}
|
12
|
+
end
|
13
|
+
def list(params=nil)
|
14
|
+
(params.is_a?(Sequel::Dataset) ? params : topics(params)).map{|t| t.thread}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
module InstanceMethods
|
18
|
+
def write_cols
|
19
|
+
super.push(:topic_id, :parent_id)
|
20
|
+
end
|
21
|
+
def children
|
22
|
+
self.class.filter(:parent_id => id).all
|
23
|
+
end
|
24
|
+
def thread
|
25
|
+
return reggae_instance if children.length==0
|
26
|
+
foo=reggae_instance
|
27
|
+
foo[2] = children.map{|m| m.thread}
|
28
|
+
foo
|
29
|
+
end
|
30
|
+
def before_save
|
31
|
+
super
|
32
|
+
self.topic_id||=self.class.max(:topic_id).to_i+1
|
33
|
+
end
|
34
|
+
def reply
|
35
|
+
foo=reggae_instance.set_values(:parent_id => id,:title => "re: #{title}")
|
36
|
+
foo.new_rec=true
|
37
|
+
foo.url=self.class.new.url
|
38
|
+
foo.schema.delete_if {|c| [:author,:id].include?(c[NAME_INDEX])}
|
39
|
+
foo
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
class MessageNav < Plugin
|
44
|
+
module ClassMethods
|
45
|
+
def section_nav
|
46
|
+
[
|
47
|
+
self.reggae_link(:new, 'New Post'),
|
48
|
+
self.reggae_link(:list, 'All Posts'),
|
49
|
+
self.reggae_link(:recent_topics, 'Recent Topics')
|
50
|
+
].push(
|
51
|
+
Marley::ReggaeMsg.new({
|
52
|
+
:title => 'Topics Tagged With:',
|
53
|
+
:description => MR::Tag.filter(:id => topics.join(:messages_tags).where(:messages__id => :message_id).select(:tag_id)).map{|t| reggae_link('list',t.tag,"#{resource_name}[tags]=#{t.tag}")}})
|
54
|
+
)
|
55
|
+
end
|
56
|
+
def section_contents
|
57
|
+
end
|
58
|
+
def recent_topics
|
59
|
+
list(:date_created > Date.today - 2)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
module Joints
|
65
|
+
class Forum < Joint
|
66
|
+
Marley.plugin('orm_rest_convenience').apply(Sequel::Model)
|
67
|
+
Marley.joint('user')
|
68
|
+
Marley.joint('messages',{:tags => true})
|
69
|
+
Marley.joint('section')
|
70
|
+
Marley.plugin(:section).apply('PrivateMessage')
|
71
|
+
Marley.plugin(:section).apply('PublicMessage')
|
72
|
+
Marley.plugin(:message_threading).apply('PublicMessage')
|
73
|
+
Marley.plugin(:message_nav).apply('PublicMessage')
|
74
|
+
class << MR::PublicMessage
|
75
|
+
def section_title;'Public Forums';end
|
76
|
+
end
|
77
|
+
module Resources
|
78
|
+
class Topic < MJ::Messages::Message
|
79
|
+
end
|
80
|
+
class TopicTag < MR::Tag
|
81
|
+
end
|
82
|
+
class Admin < MR::User
|
83
|
+
def self.requires_user?; true;end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|