dao 2.0.0 → 2.1.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 +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
|
+
}
|