dao 0.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +35 -0
- data/Rakefile +6 -3
- data/TODO +37 -0
- data/dao.gemspec +30 -0
- data/db/dao.yml +8 -0
- data/lib/dao.rb +84 -5
- data/lib/dao/active_record.rb +76 -0
- data/lib/dao/api.rb +9 -0
- data/lib/dao/api/context.rb +38 -0
- data/lib/dao/api/dsl.rb +50 -0
- data/lib/dao/api/endpoints.rb +190 -0
- data/lib/dao/api/initializers.rb +71 -0
- data/lib/dao/api/modes.rb +85 -0
- data/lib/dao/blankslate.rb +5 -0
- data/lib/dao/data.rb +58 -0
- data/lib/dao/db.rb +183 -0
- data/lib/dao/endpoint.rb +16 -0
- data/lib/dao/engine.rb +7 -0
- data/lib/dao/errors.rb +238 -0
- data/lib/dao/exceptions.rb +2 -0
- data/lib/dao/form.rb +236 -0
- data/lib/dao/mode.rb +41 -0
- data/lib/dao/mongo_mapper.rb +70 -0
- data/lib/dao/params.rb +109 -0
- data/lib/dao/path.rb +149 -0
- data/lib/dao/rails.rb +15 -0
- data/lib/dao/rails/app/api.rb +55 -0
- data/lib/dao/rails/app/controllers/api_controller.rb +99 -0
- data/lib/dao/rails/lib/generators/dao/USAGE +9 -0
- data/lib/dao/rails/lib/generators/dao/api_generator.rb +3 -0
- data/lib/dao/rails/lib/generators/dao/dao_generator.rb +27 -0
- data/lib/dao/rails/lib/generators/dao/templates/api.rb +55 -0
- data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +99 -0
- data/lib/dao/result.rb +87 -0
- data/lib/dao/slug.rb +11 -0
- data/lib/dao/status.rb +223 -0
- data/lib/dao/stdext.rb +10 -0
- data/lib/dao/support.rb +62 -0
- data/lib/dao/validations.rb +115 -0
- data/sample/rails_app/Gemfile +33 -0
- data/sample/rails_app/Gemfile.lock +88 -0
- data/sample/rails_app/README +1 -0
- data/sample/rails_app/Rakefile +7 -0
- data/sample/rails_app/app/api.rb +55 -0
- data/sample/rails_app/app/controllers/api_controller.rb +99 -0
- data/sample/rails_app/app/controllers/application_controller.rb +3 -0
- data/sample/rails_app/app/helpers/application_helper.rb +2 -0
- data/sample/rails_app/app/views/layouts/application.html.erb +14 -0
- data/sample/rails_app/config.ru +4 -0
- data/sample/rails_app/config/application.rb +51 -0
- data/sample/rails_app/config/boot.rb +13 -0
- data/sample/rails_app/config/database.yml +22 -0
- data/sample/rails_app/config/environment.rb +5 -0
- data/sample/rails_app/config/environments/development.rb +26 -0
- data/sample/rails_app/config/environments/production.rb +49 -0
- data/sample/rails_app/config/environments/test.rb +35 -0
- data/sample/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/sample/rails_app/config/initializers/inflections.rb +10 -0
- data/sample/rails_app/config/initializers/mime_types.rb +5 -0
- data/sample/rails_app/config/initializers/secret_token.rb +7 -0
- data/sample/rails_app/config/initializers/session_store.rb +8 -0
- data/sample/rails_app/config/locales/en.yml +5 -0
- data/sample/rails_app/config/routes.rb +62 -0
- data/sample/rails_app/db/development.sqlite3 +0 -0
- data/sample/rails_app/db/seeds.rb +7 -0
- data/sample/rails_app/doc/README_FOR_APP +2 -0
- data/sample/rails_app/log/development.log +27 -0
- data/sample/rails_app/log/production.log +0 -0
- data/sample/rails_app/log/server.log +0 -0
- data/sample/rails_app/log/test.log +0 -0
- data/sample/rails_app/public/404.html +26 -0
- data/sample/rails_app/public/422.html +26 -0
- data/sample/rails_app/public/500.html +26 -0
- data/sample/rails_app/public/favicon.ico +0 -0
- data/sample/rails_app/public/images/rails.png +0 -0
- data/sample/rails_app/public/index.html +239 -0
- data/sample/rails_app/public/javascripts/application.js +2 -0
- data/sample/rails_app/public/javascripts/controls.js +965 -0
- data/sample/rails_app/public/javascripts/dragdrop.js +974 -0
- data/sample/rails_app/public/javascripts/effects.js +1123 -0
- data/sample/rails_app/public/javascripts/prototype.js +6001 -0
- data/sample/rails_app/public/javascripts/rails.js +175 -0
- data/sample/rails_app/public/robots.txt +5 -0
- data/sample/rails_app/script/rails +6 -0
- data/sample/rails_app/test/performance/browsing_test.rb +9 -0
- data/sample/rails_app/test/test_helper.rb +13 -0
- data/test/dao_test.rb +271 -0
- data/test/helper.rb +15 -0
- data/test/testing.rb +74 -0
- metadata +137 -9
data/lib/dao/result.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Dao
|
2
|
+
class Result < ::Map
|
3
|
+
attr_accessor :api
|
4
|
+
attr_accessor :endpoint
|
5
|
+
attr_accessor :params
|
6
|
+
attr_accessor :validations
|
7
|
+
attr_accessor :form
|
8
|
+
|
9
|
+
def Result.for(*args, &block)
|
10
|
+
new(*args, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(*args, &block)
|
14
|
+
options = Dao.options_for!(args)
|
15
|
+
args.push('/dao') if args.empty?
|
16
|
+
|
17
|
+
path = Path.for(*args)
|
18
|
+
status = Status.ok
|
19
|
+
errors = Errors.new
|
20
|
+
data = Data.new
|
21
|
+
|
22
|
+
api = options[:api]
|
23
|
+
endpoint = options[:endpoint]
|
24
|
+
params = options[:params] || Params.new
|
25
|
+
|
26
|
+
path = endpoint.path if endpoint
|
27
|
+
|
28
|
+
form = Form.for(self)
|
29
|
+
validations = Validations.for(self)
|
30
|
+
|
31
|
+
self[:path] = path
|
32
|
+
self[:status] = status
|
33
|
+
self[:errors] = errors
|
34
|
+
self[:data] = data
|
35
|
+
|
36
|
+
@api = api
|
37
|
+
@endpoint = endpoint
|
38
|
+
@params = params
|
39
|
+
@form = form
|
40
|
+
@validations = validations
|
41
|
+
end
|
42
|
+
|
43
|
+
def path
|
44
|
+
self[:path]
|
45
|
+
end
|
46
|
+
|
47
|
+
def status(*args)
|
48
|
+
self[:status] = Status.for(*args) unless args.empty?
|
49
|
+
self[:status]
|
50
|
+
end
|
51
|
+
def status=(value)
|
52
|
+
status(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
def errors
|
56
|
+
self[:errors]
|
57
|
+
end
|
58
|
+
|
59
|
+
def data
|
60
|
+
self[:data]
|
61
|
+
end
|
62
|
+
|
63
|
+
def validates(*args, &block)
|
64
|
+
validations.add(*args, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate(*args, &block)
|
68
|
+
if !args.empty?
|
69
|
+
validates(*args, &block)
|
70
|
+
else
|
71
|
+
validations.run
|
72
|
+
#status(420) if(status.ok? and !errors.empty?)
|
73
|
+
errors.empty? and status.ok?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def valid?
|
78
|
+
validate
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate!
|
82
|
+
validations.run!
|
83
|
+
#status(420) if(status.ok? and !errors.empty?)
|
84
|
+
throw(:result, nil) unless(errors.empty? and status.ok?)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/dao/slug.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Dao
|
2
|
+
class Slug < ::String
|
3
|
+
def Slug.for(*args)
|
4
|
+
string = args.flatten.compact.join('-')
|
5
|
+
words = string.to_s.scan(%r/\w+/)
|
6
|
+
words.map!{|word| word.gsub(%r/[^0-9a-zA-Z_-]/, '')}
|
7
|
+
words.delete_if{|word| word.nil? or word.strip.empty?}
|
8
|
+
new(words.join('-').downcase)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/dao/status.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
module Dao
|
2
|
+
class Status < ::String
|
3
|
+
Code2Message = (
|
4
|
+
{
|
5
|
+
100 => "Continue",
|
6
|
+
101 => "Switching Protocols",
|
7
|
+
102 => "Processing",
|
8
|
+
|
9
|
+
200 => "OK",
|
10
|
+
201 => "Created",
|
11
|
+
202 => "Accepted",
|
12
|
+
203 => "Non-Authoritative Information",
|
13
|
+
204 => "No Content",
|
14
|
+
205 => "Reset Content",
|
15
|
+
206 => "Partial Content",
|
16
|
+
207 => "Multi-Status",
|
17
|
+
226 => "IM Used",
|
18
|
+
|
19
|
+
300 => "Multiple Choices",
|
20
|
+
301 => "Moved Permanently",
|
21
|
+
302 => "Found",
|
22
|
+
303 => "See Other",
|
23
|
+
304 => "Not Modified",
|
24
|
+
305 => "Use Proxy",
|
25
|
+
307 => "Temporary Redirect",
|
26
|
+
|
27
|
+
400 => "Bad Request",
|
28
|
+
401 => "Unauthorized",
|
29
|
+
402 => "Payment Required",
|
30
|
+
403 => "Forbidden",
|
31
|
+
404 => "Not Found",
|
32
|
+
405 => "Method Not Allowed",
|
33
|
+
406 => "Not Acceptable",
|
34
|
+
407 => "Proxy Authentication Required",
|
35
|
+
408 => "Request Timeout",
|
36
|
+
409 => "Conflict",
|
37
|
+
410 => "Gone",
|
38
|
+
411 => "Length Required",
|
39
|
+
412 => "Precondition Failed",
|
40
|
+
413 => "Request Entity Too Large",
|
41
|
+
414 => "Request-URI Too Long",
|
42
|
+
415 => "Unsupported Media Type",
|
43
|
+
416 => "Requested Range Not Satisfiable",
|
44
|
+
417 => "Expectation Failed",
|
45
|
+
420 => "Enhance Your Calm",
|
46
|
+
422 => "Unprocessable Entity",
|
47
|
+
423 => "Locked",
|
48
|
+
424 => "Failed Dependency",
|
49
|
+
426 => "Upgrade Required",
|
50
|
+
|
51
|
+
500 => "Internal Server Error",
|
52
|
+
501 => "Not Implemented",
|
53
|
+
502 => "Bad Gateway",
|
54
|
+
503 => "Service Unavailable",
|
55
|
+
504 => "Gateway Timeout",
|
56
|
+
505 => "HTTP Version Not Supported",
|
57
|
+
507 => "Insufficient Storage",
|
58
|
+
510 => "Not Extended"
|
59
|
+
}
|
60
|
+
) unless defined?(Code2Message)
|
61
|
+
|
62
|
+
def Status.underscore(camel_cased_word)
|
63
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
64
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
65
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
66
|
+
#gsub(/\s+/, '').
|
67
|
+
tr("-", "_").
|
68
|
+
downcase
|
69
|
+
end
|
70
|
+
|
71
|
+
Symbol2Code = (
|
72
|
+
Code2Message.inject(Hash.new) do |hash, (code, message)|
|
73
|
+
sym = Status.underscore(message.gsub(/\s+/, "")).to_sym
|
74
|
+
hash.update(sym => code)
|
75
|
+
end
|
76
|
+
) unless defined?(Symbol2Code)
|
77
|
+
|
78
|
+
Symbol2Code.each do |sym, code|
|
79
|
+
module_eval <<-__
|
80
|
+
def Status.#{ sym }()
|
81
|
+
@#{ sym } ||= Status.for(:#{ sym })
|
82
|
+
end
|
83
|
+
__
|
84
|
+
end
|
85
|
+
|
86
|
+
attr :code
|
87
|
+
attr :message
|
88
|
+
attr :group
|
89
|
+
|
90
|
+
def initialize(*args)
|
91
|
+
code, message =
|
92
|
+
if args.size == 2
|
93
|
+
[args.first, args.last]
|
94
|
+
else
|
95
|
+
status = Status.for(args.first || 200)
|
96
|
+
[status.code, status.message]
|
97
|
+
end
|
98
|
+
@code, @message = Integer(code), String(message)
|
99
|
+
@group = (@code / 100) * 100
|
100
|
+
replace("#{ @code } #{ @message }".strip)
|
101
|
+
end
|
102
|
+
|
103
|
+
Groups = ({
|
104
|
+
100 => 'instruction',
|
105
|
+
200 => 'success',
|
106
|
+
300 => 'redirection',
|
107
|
+
400 => 'client_error',
|
108
|
+
500 => 'server_error'
|
109
|
+
}) unless defined?(Groups)
|
110
|
+
|
111
|
+
Groups.each do |code, group|
|
112
|
+
module_eval <<-__
|
113
|
+
def #{ group }?()
|
114
|
+
#{ code } == @group
|
115
|
+
end
|
116
|
+
__
|
117
|
+
end
|
118
|
+
|
119
|
+
def Status.list
|
120
|
+
@list ||= Symbol2Code.sort_by{|sym, code| code}.map{|sym, code| send(sym)}
|
121
|
+
end
|
122
|
+
|
123
|
+
def good?
|
124
|
+
@group < 400
|
125
|
+
end
|
126
|
+
alias_method 'ok?', 'good?'
|
127
|
+
|
128
|
+
def bad?
|
129
|
+
@group >= 400
|
130
|
+
end
|
131
|
+
alias_method 'error?', 'bad?'
|
132
|
+
|
133
|
+
def ==(other)
|
134
|
+
begin
|
135
|
+
other = Status.for(other)
|
136
|
+
self.code == other.code and self.message == other.message
|
137
|
+
rescue
|
138
|
+
false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def clone
|
143
|
+
clone = Status.for(code)
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_json(*args, &block)
|
147
|
+
Map[:code, code, :message, message].to_json(*args, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
class << Status
|
151
|
+
def for(*args)
|
152
|
+
if args.size >= 2
|
153
|
+
code = args.shift
|
154
|
+
message = args.join(' ')
|
155
|
+
new(code, message)
|
156
|
+
else
|
157
|
+
arg = args.shift
|
158
|
+
case arg
|
159
|
+
when Result
|
160
|
+
result = arg
|
161
|
+
if arg.errors.nil? or arg.errors.empty? or arg.valid?
|
162
|
+
new(200)
|
163
|
+
else
|
164
|
+
new(500)
|
165
|
+
end
|
166
|
+
when Status
|
167
|
+
arg
|
168
|
+
when Fixnum
|
169
|
+
code = arg
|
170
|
+
message = Code2Message[code]
|
171
|
+
new(code, message)
|
172
|
+
when Symbol, String
|
173
|
+
sym = Status.underscore(arg).to_sym
|
174
|
+
code = Symbol2Code[sym]
|
175
|
+
if code
|
176
|
+
message = Code2Message[code]
|
177
|
+
else
|
178
|
+
code = 500
|
179
|
+
message = "Unknown Status #{ arg }"
|
180
|
+
end
|
181
|
+
new(code, message)
|
182
|
+
else
|
183
|
+
if arg.respond_to?(:code) and arg.respond_to?(:message)
|
184
|
+
code, message = arg.code, arg.message
|
185
|
+
new(code, message)
|
186
|
+
else
|
187
|
+
parse(arg)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def parse(string)
|
194
|
+
first, last = string.to_s.strip.split(%r/\s+/, 2)
|
195
|
+
if first =~ %r/^\d+$/
|
196
|
+
code = Integer(first)
|
197
|
+
message = last
|
198
|
+
else
|
199
|
+
code = 500
|
200
|
+
message = "Unknown Status #{ string.inspect }"
|
201
|
+
end
|
202
|
+
new(code, message)
|
203
|
+
end
|
204
|
+
|
205
|
+
def cast(*args)
|
206
|
+
if args.size == 1
|
207
|
+
value = args.first
|
208
|
+
value.is_a?(self) ? value : self.for(value)
|
209
|
+
else
|
210
|
+
self.for(*args)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def Dao.status(*args, &block)
|
217
|
+
if args.empty? and block.nil?
|
218
|
+
Status
|
219
|
+
else
|
220
|
+
Status.for(*args, &block)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/dao/stdext.rb
ADDED
data/lib/dao/support.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Dao
|
2
|
+
def map_for(*args, &block)
|
3
|
+
Map.for(*args, &block)
|
4
|
+
end
|
5
|
+
alias_method(:map, :map_for)
|
6
|
+
alias_method(:hash, :map_for)
|
7
|
+
|
8
|
+
def options_for!(args)
|
9
|
+
Map.options_for!(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def options_for(args)
|
13
|
+
Map.options_for(args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def db(*args, &block)
|
17
|
+
if args.empty? and block.nil?
|
18
|
+
Db.instance
|
19
|
+
else
|
20
|
+
method = args.shift
|
21
|
+
Db.instance.send(method, *args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
%w( to_dao as_dao ).each do |method|
|
26
|
+
|
27
|
+
module_eval <<-__, __FILE__, __LINE__ - 1
|
28
|
+
|
29
|
+
def #{ method }(object, *args, &block)
|
30
|
+
case object
|
31
|
+
when Array
|
32
|
+
object.map{|element| Dao.#{ method }(element)}
|
33
|
+
|
34
|
+
else
|
35
|
+
if object.respond_to?(:#{ method })
|
36
|
+
object.send(:#{ method }, *args, &block)
|
37
|
+
else
|
38
|
+
object
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
__
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def unindent!(s)
|
48
|
+
margin = nil
|
49
|
+
s.each do |line|
|
50
|
+
next if line =~ %r/^\s*$/
|
51
|
+
margin = line[%r/^\s*/] and break
|
52
|
+
end
|
53
|
+
s.gsub! %r/^#{ margin }/, "" if margin
|
54
|
+
margin ? s : nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def unindent(s)
|
58
|
+
s = "#{ s }"
|
59
|
+
unindent!(s)
|
60
|
+
s
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Dao
|
2
|
+
class Validations < ::Map
|
3
|
+
class Callback < ::Proc
|
4
|
+
attr :options
|
5
|
+
|
6
|
+
def initialize(options = {}, &block)
|
7
|
+
@options = Dao.map_for(options || {})
|
8
|
+
super(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def block
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << Validations
|
17
|
+
def for(*args, &block)
|
18
|
+
new(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cast(*args)
|
22
|
+
if args.size == 1
|
23
|
+
value = args.first
|
24
|
+
value.is_a?(self) ? value : self.for(value)
|
25
|
+
else
|
26
|
+
self.for(*args)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :result
|
32
|
+
|
33
|
+
def initialize(*args, &block)
|
34
|
+
@result = args.shift if args.first.is_a?(Result)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def params
|
39
|
+
result.params
|
40
|
+
end
|
41
|
+
|
42
|
+
def errors
|
43
|
+
result.errors
|
44
|
+
end
|
45
|
+
|
46
|
+
def each(&block)
|
47
|
+
depth_first_each(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def size
|
51
|
+
size = 0
|
52
|
+
depth_first_each{ size += 1 }
|
53
|
+
size
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method 'count', 'size'
|
57
|
+
alias_method 'length', 'size'
|
58
|
+
|
59
|
+
Cleared = '___CLEARED___'.freeze unless defined?(Cleared)
|
60
|
+
|
61
|
+
def run
|
62
|
+
previous_errors = []
|
63
|
+
new_errors = []
|
64
|
+
|
65
|
+
errors.each_message do |keys, message|
|
66
|
+
previous_errors.push([keys, message])
|
67
|
+
end
|
68
|
+
|
69
|
+
errors.clear
|
70
|
+
|
71
|
+
depth_first_each do |keys, callback|
|
72
|
+
next unless callback and callback.respond_to?(:to_proc)
|
73
|
+
|
74
|
+
value = params.get(keys)
|
75
|
+
valid = !!callback.call(value)
|
76
|
+
#valid = !!params.instance_exec(value, &callback)
|
77
|
+
message = callback.options[:message] || (value.nil? ? 'is blank.' : 'is invalid.')
|
78
|
+
|
79
|
+
unless valid
|
80
|
+
new_errors.push([keys, message])
|
81
|
+
else
|
82
|
+
new_errors.push([keys, Cleared])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
previous_errors.each do |keys, message|
|
87
|
+
errors.add(keys, message) unless new_errors.assoc(keys)
|
88
|
+
end
|
89
|
+
|
90
|
+
new_errors.each do |keys, value|
|
91
|
+
next if value == Cleared
|
92
|
+
message = value
|
93
|
+
errors.add(keys, message)
|
94
|
+
end
|
95
|
+
|
96
|
+
return self
|
97
|
+
end
|
98
|
+
|
99
|
+
def run!
|
100
|
+
errors.clear!
|
101
|
+
run
|
102
|
+
end
|
103
|
+
|
104
|
+
NotNil = lambda{|value| !value.nil?}
|
105
|
+
|
106
|
+
def add(*args, &block)
|
107
|
+
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
108
|
+
block = args.pop if args.last.respond_to?(:call)
|
109
|
+
block ||= NotNil
|
110
|
+
callback = Validations::Callback.new(options, &block)
|
111
|
+
args.push(callback)
|
112
|
+
set(*args)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|