dao 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +96 -17
- data/Rakefile +92 -43
- data/dao.gemspec +12 -8
- data/lib/dao.rb +33 -20
- data/lib/dao/active_record.rb +51 -43
- data/lib/dao/api.rb +1 -1
- data/lib/dao/api/context.rb +6 -6
- data/lib/dao/api/dsl.rb +2 -2
- data/lib/dao/api/interfaces.rb +232 -0
- data/lib/dao/api/modes.rb +9 -6
- data/lib/dao/data.rb +7 -0
- data/lib/dao/errors.rb +27 -30
- data/lib/dao/form.rb +52 -28
- data/lib/dao/instance_exec.rb +37 -0
- data/lib/dao/interface.rb +16 -0
- data/lib/dao/params.rb +48 -24
- data/lib/dao/path.rb +1 -1
- data/lib/dao/rails.rb +5 -2
- data/lib/dao/rails/app/api.rb +1 -1
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +12 -0
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +7 -1
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +25 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.css +27 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao.js +149 -0
- data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +10 -0
- data/lib/dao/result.rb +53 -14
- data/lib/dao/status.rb +162 -1
- data/lib/dao/stdext.rb +6 -0
- data/lib/dao/support.rb +10 -2
- data/lib/dao/validations.rb +338 -14
- data/sample/rails_app/pubic/javascripts/dao.js +148 -0
- data/test/dao_test.rb +18 -19
- metadata +29 -18
- data/TODO +0 -37
- data/db/dao.yml +0 -8
@@ -0,0 +1,37 @@
|
|
1
|
+
module Dao
|
2
|
+
module InstanceExec
|
3
|
+
Code = lambda do
|
4
|
+
unless Object.new.respond_to?(:instance_exec)
|
5
|
+
module InstanceExecHelper; end
|
6
|
+
include InstanceExecHelper
|
7
|
+
|
8
|
+
def instance_exec(*args, &block)
|
9
|
+
begin
|
10
|
+
old_critical, Thread.critical = Thread.critical, true
|
11
|
+
n = 0
|
12
|
+
n += 1 while respond_to?(mname="__instance_exec_#{ n }__")
|
13
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
14
|
+
ensure
|
15
|
+
Thread.critical = old_critical
|
16
|
+
end
|
17
|
+
begin
|
18
|
+
ret = send(mname, *args)
|
19
|
+
ensure
|
20
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
21
|
+
end
|
22
|
+
ret
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def InstanceExec.included(other)
|
28
|
+
other.module_eval(&Code)
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def InstanceExec.extend_object(other)
|
33
|
+
other.instance_eval(&Code)
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dao
|
2
|
+
class Interface
|
3
|
+
Attrs = %w( api path method doc )
|
4
|
+
Attrs.each{|attr| attr_accessor(attr)}
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
update(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update(options = {})
|
11
|
+
options.each do |key, val|
|
12
|
+
send("#{ key }=", val)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/dao/params.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Dao
|
2
2
|
class Params < ::Map
|
3
|
+
include Dao::InstanceExec
|
4
|
+
|
3
5
|
class << Params
|
4
6
|
def parse(prefix, params = {})
|
5
7
|
prefix = prefix.to_s
|
@@ -24,7 +26,7 @@ module Dao
|
|
24
26
|
end
|
25
27
|
|
26
28
|
attr_accessor :api
|
27
|
-
attr_accessor :
|
29
|
+
attr_accessor :interface
|
28
30
|
attr_accessor :params
|
29
31
|
attr_accessor :result
|
30
32
|
|
@@ -32,12 +34,12 @@ module Dao
|
|
32
34
|
options = Dao.options_for!(args)
|
33
35
|
|
34
36
|
api = options[:api]
|
35
|
-
|
37
|
+
interface = options[:interface]
|
36
38
|
updates = options[:params]
|
37
39
|
|
38
40
|
params = new()
|
39
41
|
params.api = api
|
40
|
-
params.
|
42
|
+
params.interface = interface
|
41
43
|
|
42
44
|
params.update(updates) if updates
|
43
45
|
|
@@ -78,32 +80,54 @@ module Dao
|
|
78
80
|
def validate!
|
79
81
|
result.validate! if result
|
80
82
|
end
|
83
|
+
end
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
99
|
-
end
|
100
|
-
ret
|
85
|
+
def Dao.parse(path, params)
|
86
|
+
return params if params.is_a?(Params)
|
87
|
+
|
88
|
+
smells_like_unparsed_rails_params = (
|
89
|
+
params.has_key?(:action) and
|
90
|
+
params.has_key?(:controller) and
|
91
|
+
params.class.name =~ /HashWithIndifferentAccess/
|
92
|
+
)
|
93
|
+
return(Params.parse(path.to_s, params)) if smells_like_unparsed_rails_params
|
94
|
+
|
95
|
+
smells_like_unparsed_params = false
|
96
|
+
unparsed_re = %r/^#{ Regexp.escape(path.to_s) }/
|
97
|
+
params.each do |key, val|
|
98
|
+
if key =~ unparsed_re
|
99
|
+
smells_like_unparsed_params = true
|
100
|
+
break
|
101
101
|
end
|
102
102
|
end
|
103
|
+
return(Params.parse(path.to_s, params)) if smells_like_unparsed_params
|
104
|
+
|
105
|
+
return Params.new(params)
|
103
106
|
end
|
104
107
|
|
108
|
+
=begin
|
109
|
+
def Dao.parse(path, params)
|
110
|
+
returned = Params.new(params)
|
111
|
+
|
112
|
+
smells_like_rails = (params.has_key?(:action) and params.has_key?(:controller) and params.class.name =~ /HashWithIndifferentAccess/)
|
113
|
+
if smells_like_rails
|
114
|
+
returned.delete(:action)
|
115
|
+
returned.delete(:controller)
|
116
|
+
returned.update(Params.parse(path, params))
|
117
|
+
return returned
|
118
|
+
end
|
119
|
+
|
120
|
+
re = %r/^#{ Regexp.escape(path) }/
|
121
|
+
smells_like_unparsed = false
|
122
|
+
params.each do |key, val|
|
123
|
+
break(smells_like_unparsed = true) if key =~ re
|
124
|
+
end
|
125
|
+
if smells_like_unparsed
|
126
|
+
returned.update(Params.parse(path, params))
|
127
|
+
return returned
|
128
|
+
end
|
105
129
|
|
106
|
-
|
107
|
-
Params.parse(*args, &block)
|
130
|
+
return returned
|
108
131
|
end
|
132
|
+
=end
|
109
133
|
end
|
data/lib/dao/path.rb
CHANGED
data/lib/dao/rails.rb
CHANGED
@@ -3,13 +3,16 @@ if defined?(Rails)
|
|
3
3
|
class Engine < Rails::Engine
|
4
4
|
GEM_DIR = File.expand_path(__FILE__ + '/../../../')
|
5
5
|
ROOT_DIR = File.join(GEM_DIR, 'lib/dao/rails')
|
6
|
-
#APP_DIR = File.join(ROOT_DIR, 'app')
|
7
6
|
|
8
|
-
### https://gist.github.com/af7e572c2dc973add221
|
7
|
+
### ref: https://gist.github.com/af7e572c2dc973add221
|
9
8
|
|
10
9
|
paths.path = ROOT_DIR
|
11
10
|
### config.autoload_paths << APP_DIR
|
12
11
|
### $LOAD_PATH.push(File.join(Rails.root.to_s, 'app'))
|
12
|
+
|
13
|
+
#config.after_initialize do
|
14
|
+
#unloadable(Dao)
|
15
|
+
#end
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
data/lib/dao/rails/app/api.rb
CHANGED
@@ -6,6 +6,12 @@ class DaoGenerator < Rails::Generators::NamedBase
|
|
6
6
|
|
7
7
|
copy_file("api_controller.rb", "app/controllers/api_controller.rb")
|
8
8
|
|
9
|
+
copy_file("dao_helper.rb", "app/helpers/dao_helper.rb")
|
10
|
+
|
11
|
+
copy_file("dao.js", "public/javascripts/dao.js")
|
12
|
+
|
13
|
+
copy_file("dao.css", "public/stylesheets/dao.css")
|
14
|
+
|
9
15
|
route("match 'api/*path' => 'api#call', :as => 'api'")
|
10
16
|
route("match 'api' => 'api#index', :as => 'api_index'")
|
11
17
|
|
@@ -21,6 +27,12 @@ class DaoGenerator < Rails::Generators::NamedBase
|
|
21
27
|
|
22
28
|
config.autoload_paths += %w( app )
|
23
29
|
|
30
|
+
### config.action_view.javascript_expansions[:defaults] ||= []
|
31
|
+
### config.action_view.javascript_expansions[:defaults] += %( dao )
|
32
|
+
|
33
|
+
### config.action_view.stylesheet_expansions[:defaults] ||= []
|
34
|
+
### config.action_view.stylesheet_expansions[:defaults] += %( dao )
|
35
|
+
|
24
36
|
__
|
25
37
|
)
|
26
38
|
end
|
@@ -2,7 +2,7 @@ Api =
|
|
2
2
|
Dao.api do
|
3
3
|
|
4
4
|
description 'ping!'
|
5
|
-
|
5
|
+
interface('/ping'){
|
6
6
|
data.update :time => Time.now
|
7
7
|
}
|
8
8
|
|
@@ -29,6 +29,8 @@ Api =
|
|
29
29
|
|
30
30
|
alias_method('user', 'effective_user')
|
31
31
|
alias_method('user=', 'effective_user=')
|
32
|
+
alias_method('current_user', 'effective_user')
|
33
|
+
alias_method('current_user=', 'effective_user=')
|
32
34
|
|
33
35
|
def api
|
34
36
|
self
|
@@ -53,3 +55,7 @@ Api =
|
|
53
55
|
|
54
56
|
|
55
57
|
unloadable(Api)
|
58
|
+
|
59
|
+
def api(*args, &block)
|
60
|
+
Api.new(*args, &block)
|
61
|
+
end
|
@@ -4,8 +4,11 @@ class APIController < ApplicationController
|
|
4
4
|
skip_before_filter true
|
5
5
|
skip_before_filter :verify_authenticity_token
|
6
6
|
|
7
|
+
before_filter :setup_path
|
7
8
|
before_filter :setup_api
|
8
9
|
|
10
|
+
WhiteList = %w( ping index module )
|
11
|
+
|
9
12
|
### skip_before_filter :set_current_user if Rails.env.production?
|
10
13
|
|
11
14
|
##
|
@@ -52,7 +55,20 @@ protected
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
58
|
+
def setup_path
|
59
|
+
@path = params[:path]
|
60
|
+
end
|
61
|
+
|
62
|
+
def path
|
63
|
+
@path
|
64
|
+
end
|
65
|
+
|
55
66
|
def setup_api
|
67
|
+
if white_listed?(path)
|
68
|
+
@api = Api.new
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
56
72
|
email, password = http_basic_auth_info
|
57
73
|
|
58
74
|
if !email.blank? and !password.blank?
|
@@ -80,6 +96,15 @@ protected
|
|
80
96
|
@api
|
81
97
|
end
|
82
98
|
|
99
|
+
def self.white_listed?(path)
|
100
|
+
@white_listed ||= ( WhiteList.inject(Hash.new){|hash, path| hash.update(path.to_s => true)} )
|
101
|
+
@white_listed[path.to_s]
|
102
|
+
end
|
103
|
+
|
104
|
+
def white_listed?(path)
|
105
|
+
self.class.white_listed?(path)
|
106
|
+
end
|
107
|
+
|
83
108
|
def http_basic_auth
|
84
109
|
@http_basic_auth ||= (
|
85
110
|
request.env['HTTP_AUTHORIZATION'] ||
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/* dao errors */
|
2
|
+
table.dao.errors {
|
3
|
+
font-weight: normal;
|
4
|
+
width: 100%;
|
5
|
+
border-bottom: 1px solid #ccc;
|
6
|
+
}
|
7
|
+
table.dao.errors caption {
|
8
|
+
font-weight: bold;
|
9
|
+
color: #333;
|
10
|
+
font-style: italic;
|
11
|
+
text-align: left;
|
12
|
+
margin-bottom: 0.5em;
|
13
|
+
}
|
14
|
+
table.dao.errors td.key {
|
15
|
+
padding-left: 1em;
|
16
|
+
color: #666;
|
17
|
+
}
|
18
|
+
table.dao.errors td.separator {
|
19
|
+
color: #666;
|
20
|
+
padding-left: 0.5em;
|
21
|
+
padding-right: 0.5em;
|
22
|
+
}
|
23
|
+
table.dao.errors td.message {
|
24
|
+
font-style: italic;
|
25
|
+
width: 100%;
|
26
|
+
}
|
27
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
if(!window.Dao){
|
2
|
+
(function(){
|
3
|
+
window.Dao = {};
|
4
|
+
|
5
|
+
// pull jQuery off the CDN iff needed
|
6
|
+
//
|
7
|
+
!window.jQuery && document.write(unescape('%3Cscript src="js/libs/jquery-1.4.2.js"%3E%3C/script%3E'));
|
8
|
+
var jq = jQuery;
|
9
|
+
|
10
|
+
// ctor
|
11
|
+
//
|
12
|
+
Dao.Api = function(){
|
13
|
+
this.route = '' + Dao.Api.route;
|
14
|
+
this.result = null;
|
15
|
+
this.results = [];
|
16
|
+
this.mode = 'get';
|
17
|
+
this.method = Dao[this.mode];
|
18
|
+
};
|
19
|
+
Dao.Api.route = '/api';
|
20
|
+
Dao.Api.modes = ["options", "get", "head", "post", "put", "delete", "trace", "connect"];
|
21
|
+
|
22
|
+
// single call interface
|
23
|
+
//
|
24
|
+
Dao.Api.prototype.call = function(){
|
25
|
+
var api = this;
|
26
|
+
var options = {};
|
27
|
+
|
28
|
+
if(arguments.length == 1){
|
29
|
+
var arg = arguments[0];
|
30
|
+
|
31
|
+
if(typeof(arg)=='string'){
|
32
|
+
options.path = arg;
|
33
|
+
} else {
|
34
|
+
options = arg;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
if(arguments.length > 1){
|
39
|
+
options.path = arguments[0];
|
40
|
+
options.params = arguments[1];
|
41
|
+
options.success = arguments[2];
|
42
|
+
}
|
43
|
+
|
44
|
+
if(!options.path){
|
45
|
+
options.path = '/ping';
|
46
|
+
}
|
47
|
+
|
48
|
+
if(!options.params){
|
49
|
+
options.params = {};
|
50
|
+
}
|
51
|
+
|
52
|
+
if(!options.success){
|
53
|
+
options.success = function(result){
|
54
|
+
result = new Dao.Result(result);
|
55
|
+
api.result = result;
|
56
|
+
api.results.push(result);
|
57
|
+
};
|
58
|
+
}
|
59
|
+
|
60
|
+
var url = api.route + options.path;
|
61
|
+
|
62
|
+
var data = options.params;
|
63
|
+
|
64
|
+
var success = function(result){
|
65
|
+
var result = new Dao.Result(result);
|
66
|
+
if(options.success){
|
67
|
+
options.success(result);
|
68
|
+
} else {
|
69
|
+
api.result = result;
|
70
|
+
api.results.push(result);
|
71
|
+
}
|
72
|
+
};
|
73
|
+
|
74
|
+
var ajax = {};
|
75
|
+
ajax.url = url;
|
76
|
+
ajax.data = data;
|
77
|
+
ajax.success = success;
|
78
|
+
|
79
|
+
Dao[api.mode](ajax);
|
80
|
+
return(api);
|
81
|
+
};
|
82
|
+
|
83
|
+
// meta-program api.read(..), api.post(...), ...
|
84
|
+
//
|
85
|
+
for(var i = 0; i < Dao.Api.modes.length; i++){
|
86
|
+
(function(){
|
87
|
+
var mode = Dao.Api.modes[i];
|
88
|
+
|
89
|
+
Dao.Api.prototype[mode] = function(){
|
90
|
+
var api = this;
|
91
|
+
var previous = api.mode;
|
92
|
+
api.mode = mode;
|
93
|
+
var returned = api.call.apply(api, arguments);
|
94
|
+
api.mode = previous;
|
95
|
+
return(returned);
|
96
|
+
};
|
97
|
+
})();
|
98
|
+
}
|
99
|
+
Dao.Api.prototype['read'] = Dao.Api.prototype['get'];
|
100
|
+
Dao.Api.prototype['write'] = Dao.Api.prototype['post'];
|
101
|
+
|
102
|
+
// a thin wrapper on results for now. TODO - make it smarter
|
103
|
+
//
|
104
|
+
Dao.Result = function(options){
|
105
|
+
this.path = options.path;
|
106
|
+
this.status = options.status;
|
107
|
+
this.errors = options.errors;
|
108
|
+
this.data = options.data;
|
109
|
+
|
110
|
+
//parts = ('' + this.status).split(/\s+/);
|
111
|
+
//this.status.code = parseInt(parts.shift());
|
112
|
+
//this.status.message = parts.join(' ');
|
113
|
+
};
|
114
|
+
|
115
|
+
// ajax utils
|
116
|
+
//
|
117
|
+
Dao.ajax = function(options){
|
118
|
+
var ajax = {};
|
119
|
+
ajax.type = options.type;
|
120
|
+
ajax.url = options.url;
|
121
|
+
ajax.dataType = 'json';
|
122
|
+
ajax.cache = false;
|
123
|
+
if(ajax.type == 'POST' || ajax.type == 'PUT'){
|
124
|
+
ajax.data = jq.toJSON(options.data || {});
|
125
|
+
} else {
|
126
|
+
ajax.data = (options.data || {});
|
127
|
+
};
|
128
|
+
ajax.contentType = (options.contentType || 'application/json; charset=utf-8');
|
129
|
+
ajax.success = (options.success || function(){});
|
130
|
+
jq.ajax(ajax);
|
131
|
+
};
|
132
|
+
|
133
|
+
// meta-program Api.get(...), Api.post(...)
|
134
|
+
//
|
135
|
+
for(var i = 0; i < Dao.Api.modes.length; i++){
|
136
|
+
(function(){
|
137
|
+
var mode = Dao.Api.modes[i];
|
138
|
+
|
139
|
+
Dao[mode] = function(options){
|
140
|
+
options.type = mode.toUpperCase();
|
141
|
+
Dao.ajax(options);
|
142
|
+
};
|
143
|
+
})();
|
144
|
+
}
|
145
|
+
|
146
|
+
Dao.api = new Dao.Api();
|
147
|
+
window.api = window.api || Dao.api;
|
148
|
+
}());
|
149
|
+
}
|