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.
Files changed (90) hide show
  1. data/README +35 -0
  2. data/Rakefile +6 -3
  3. data/TODO +37 -0
  4. data/dao.gemspec +30 -0
  5. data/db/dao.yml +8 -0
  6. data/lib/dao.rb +84 -5
  7. data/lib/dao/active_record.rb +76 -0
  8. data/lib/dao/api.rb +9 -0
  9. data/lib/dao/api/context.rb +38 -0
  10. data/lib/dao/api/dsl.rb +50 -0
  11. data/lib/dao/api/endpoints.rb +190 -0
  12. data/lib/dao/api/initializers.rb +71 -0
  13. data/lib/dao/api/modes.rb +85 -0
  14. data/lib/dao/blankslate.rb +5 -0
  15. data/lib/dao/data.rb +58 -0
  16. data/lib/dao/db.rb +183 -0
  17. data/lib/dao/endpoint.rb +16 -0
  18. data/lib/dao/engine.rb +7 -0
  19. data/lib/dao/errors.rb +238 -0
  20. data/lib/dao/exceptions.rb +2 -0
  21. data/lib/dao/form.rb +236 -0
  22. data/lib/dao/mode.rb +41 -0
  23. data/lib/dao/mongo_mapper.rb +70 -0
  24. data/lib/dao/params.rb +109 -0
  25. data/lib/dao/path.rb +149 -0
  26. data/lib/dao/rails.rb +15 -0
  27. data/lib/dao/rails/app/api.rb +55 -0
  28. data/lib/dao/rails/app/controllers/api_controller.rb +99 -0
  29. data/lib/dao/rails/lib/generators/dao/USAGE +9 -0
  30. data/lib/dao/rails/lib/generators/dao/api_generator.rb +3 -0
  31. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +27 -0
  32. data/lib/dao/rails/lib/generators/dao/templates/api.rb +55 -0
  33. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +99 -0
  34. data/lib/dao/result.rb +87 -0
  35. data/lib/dao/slug.rb +11 -0
  36. data/lib/dao/status.rb +223 -0
  37. data/lib/dao/stdext.rb +10 -0
  38. data/lib/dao/support.rb +62 -0
  39. data/lib/dao/validations.rb +115 -0
  40. data/sample/rails_app/Gemfile +33 -0
  41. data/sample/rails_app/Gemfile.lock +88 -0
  42. data/sample/rails_app/README +1 -0
  43. data/sample/rails_app/Rakefile +7 -0
  44. data/sample/rails_app/app/api.rb +55 -0
  45. data/sample/rails_app/app/controllers/api_controller.rb +99 -0
  46. data/sample/rails_app/app/controllers/application_controller.rb +3 -0
  47. data/sample/rails_app/app/helpers/application_helper.rb +2 -0
  48. data/sample/rails_app/app/views/layouts/application.html.erb +14 -0
  49. data/sample/rails_app/config.ru +4 -0
  50. data/sample/rails_app/config/application.rb +51 -0
  51. data/sample/rails_app/config/boot.rb +13 -0
  52. data/sample/rails_app/config/database.yml +22 -0
  53. data/sample/rails_app/config/environment.rb +5 -0
  54. data/sample/rails_app/config/environments/development.rb +26 -0
  55. data/sample/rails_app/config/environments/production.rb +49 -0
  56. data/sample/rails_app/config/environments/test.rb +35 -0
  57. data/sample/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  58. data/sample/rails_app/config/initializers/inflections.rb +10 -0
  59. data/sample/rails_app/config/initializers/mime_types.rb +5 -0
  60. data/sample/rails_app/config/initializers/secret_token.rb +7 -0
  61. data/sample/rails_app/config/initializers/session_store.rb +8 -0
  62. data/sample/rails_app/config/locales/en.yml +5 -0
  63. data/sample/rails_app/config/routes.rb +62 -0
  64. data/sample/rails_app/db/development.sqlite3 +0 -0
  65. data/sample/rails_app/db/seeds.rb +7 -0
  66. data/sample/rails_app/doc/README_FOR_APP +2 -0
  67. data/sample/rails_app/log/development.log +27 -0
  68. data/sample/rails_app/log/production.log +0 -0
  69. data/sample/rails_app/log/server.log +0 -0
  70. data/sample/rails_app/log/test.log +0 -0
  71. data/sample/rails_app/public/404.html +26 -0
  72. data/sample/rails_app/public/422.html +26 -0
  73. data/sample/rails_app/public/500.html +26 -0
  74. data/sample/rails_app/public/favicon.ico +0 -0
  75. data/sample/rails_app/public/images/rails.png +0 -0
  76. data/sample/rails_app/public/index.html +239 -0
  77. data/sample/rails_app/public/javascripts/application.js +2 -0
  78. data/sample/rails_app/public/javascripts/controls.js +965 -0
  79. data/sample/rails_app/public/javascripts/dragdrop.js +974 -0
  80. data/sample/rails_app/public/javascripts/effects.js +1123 -0
  81. data/sample/rails_app/public/javascripts/prototype.js +6001 -0
  82. data/sample/rails_app/public/javascripts/rails.js +175 -0
  83. data/sample/rails_app/public/robots.txt +5 -0
  84. data/sample/rails_app/script/rails +6 -0
  85. data/sample/rails_app/test/performance/browsing_test.rb +9 -0
  86. data/sample/rails_app/test/test_helper.rb +13 -0
  87. data/test/dao_test.rb +271 -0
  88. data/test/helper.rb +15 -0
  89. data/test/testing.rb +74 -0
  90. 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
@@ -0,0 +1,10 @@
1
+ class Array
2
+ def to_dao(*args, &block)
3
+ Dao.to_dao(self, *args, &block)
4
+ end
5
+
6
+ def as_dao(*args, &block)
7
+ Dao.as_dao(self, *args, &block)
8
+ end
9
+ end
10
+
@@ -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