marley 0.5.0 → 0.6.0
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/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
|