dao 0.0.0 → 2.0.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 +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
|