dao 2.2.3 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/Rakefile +68 -113
  2. data/TODO +3 -9
  3. data/a.rb +25 -0
  4. data/dao.gemspec +70 -14
  5. data/lib/dao.rb +8 -8
  6. data/lib/dao/api.rb +1 -0
  7. data/lib/dao/api/context.rb +48 -23
  8. data/lib/dao/api/dsl.rb +1 -1
  9. data/lib/dao/api/interfaces.rb +149 -117
  10. data/lib/dao/api/modes.rb +24 -23
  11. data/lib/dao/api/routes.rb +9 -0
  12. data/lib/dao/data.rb +9 -2
  13. data/lib/dao/errors.rb +15 -11
  14. data/lib/dao/form.rb +46 -37
  15. data/lib/dao/interface.rb +1 -1
  16. data/lib/dao/mode.rb +45 -20
  17. data/lib/dao/params.rb +35 -53
  18. data/lib/dao/path.rb +6 -9
  19. data/lib/dao/rails/lib/generators/dao/templates/api.rb +1 -1
  20. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +16 -0
  21. data/lib/dao/result.rb +26 -133
  22. data/lib/dao/route.rb +87 -0
  23. data/lib/dao/status.rb +96 -94
  24. data/lib/dao/support.rb +5 -3
  25. data/lib/dao/validations.rb +46 -442
  26. data/lib/dao/validations/base.rb +68 -0
  27. data/lib/dao/validations/common.rb +463 -0
  28. data/test/dao_test.rb +214 -33
  29. metadata +20 -112
  30. data/lib/dao/rails/app/api.rb +0 -55
  31. data/lib/dao/rails/app/controllers/api_controller.rb +0 -99
  32. data/sample/rails_app/Gemfile +0 -33
  33. data/sample/rails_app/Gemfile.lock +0 -88
  34. data/sample/rails_app/README +0 -1
  35. data/sample/rails_app/Rakefile +0 -7
  36. data/sample/rails_app/app/api.rb +0 -55
  37. data/sample/rails_app/app/controllers/api_controller.rb +0 -99
  38. data/sample/rails_app/app/controllers/application_controller.rb +0 -3
  39. data/sample/rails_app/app/helpers/application_helper.rb +0 -2
  40. data/sample/rails_app/app/views/layouts/application.html.erb +0 -14
  41. data/sample/rails_app/config.ru +0 -4
  42. data/sample/rails_app/config/application.rb +0 -51
  43. data/sample/rails_app/config/boot.rb +0 -13
  44. data/sample/rails_app/config/database.yml +0 -22
  45. data/sample/rails_app/config/environment.rb +0 -5
  46. data/sample/rails_app/config/environments/development.rb +0 -26
  47. data/sample/rails_app/config/environments/production.rb +0 -49
  48. data/sample/rails_app/config/environments/test.rb +0 -35
  49. data/sample/rails_app/config/initializers/backtrace_silencers.rb +0 -7
  50. data/sample/rails_app/config/initializers/inflections.rb +0 -10
  51. data/sample/rails_app/config/initializers/mime_types.rb +0 -5
  52. data/sample/rails_app/config/initializers/secret_token.rb +0 -7
  53. data/sample/rails_app/config/initializers/session_store.rb +0 -8
  54. data/sample/rails_app/config/locales/en.yml +0 -5
  55. data/sample/rails_app/config/routes.rb +0 -62
  56. data/sample/rails_app/db/development.sqlite3 +0 -0
  57. data/sample/rails_app/db/seeds.rb +0 -7
  58. data/sample/rails_app/doc/README_FOR_APP +0 -2
  59. data/sample/rails_app/log/development.log +0 -27
  60. data/sample/rails_app/log/production.log +0 -0
  61. data/sample/rails_app/log/server.log +0 -0
  62. data/sample/rails_app/log/test.log +0 -0
  63. data/sample/rails_app/pubic/javascripts/dao.js +0 -148
  64. data/sample/rails_app/public/404.html +0 -26
  65. data/sample/rails_app/public/422.html +0 -26
  66. data/sample/rails_app/public/500.html +0 -26
  67. data/sample/rails_app/public/favicon.ico +0 -0
  68. data/sample/rails_app/public/images/rails.png +0 -0
  69. data/sample/rails_app/public/index.html +0 -239
  70. data/sample/rails_app/public/javascripts/application.js +0 -2
  71. data/sample/rails_app/public/javascripts/controls.js +0 -965
  72. data/sample/rails_app/public/javascripts/dragdrop.js +0 -974
  73. data/sample/rails_app/public/javascripts/effects.js +0 -1123
  74. data/sample/rails_app/public/javascripts/prototype.js +0 -6001
  75. data/sample/rails_app/public/javascripts/rails.js +0 -175
  76. data/sample/rails_app/public/robots.txt +0 -5
  77. data/sample/rails_app/script/rails +0 -6
  78. data/sample/rails_app/test/performance/browsing_test.rb +0 -9
  79. data/sample/rails_app/test/test_helper.rb +0 -13
@@ -1,7 +1,11 @@
1
1
  module Dao
2
2
  class Params < ::Map
3
- include Dao::InstanceExec
3
+ # mixins
4
+ #
5
+ include Validations::Mixin
4
6
 
7
+ # class methods
8
+ #
5
9
  class << Params
6
10
  def parse(prefix, params = {}, options = {})
7
11
  prefix = prefix.to_s
@@ -60,64 +64,42 @@ module Dao
60
64
  end
61
65
  end
62
66
 
63
- attr_accessor :api
64
- attr_accessor :interface
65
- attr_accessor :params
67
+ # instance methods
68
+ #
66
69
  attr_accessor :result
67
-
68
- def Params.for(*args, &block)
69
- options = Dao.options_for!(args)
70
-
71
- api = options[:api]
72
- interface = options[:interface]
73
- updates = options[:params]
74
-
75
- params = new()
76
- params.api = api
77
- params.interface = interface
78
-
79
- params.update(updates) if updates
80
-
81
- params
82
- end
83
-
84
- def path
85
- result.path if result
86
- end
87
-
88
- def status(*args)
89
- result.status(*args) if result
90
- end
91
- def status=(value)
92
- result.status=value if result
70
+ attr_accessor :route
71
+ attr_accessor :path
72
+ attr_accessor :status
73
+ attr_accessor :errors
74
+ attr_accessor :validations
75
+ attr_accessor :form
76
+
77
+ def initialize(*args, &block)
78
+ @path = Path.default
79
+ @status = Status.default
80
+ @errors = Errors.new
81
+ @validations = Validations.for(self)
82
+ @form = Form.for(self)
83
+ super
93
84
  end
94
85
 
95
- def errors
96
- result.errors if result
86
+ # look good for inspect
87
+ #
88
+ def inspect
89
+ ::JSON.pretty_generate(self, :max_nesting => 0)
97
90
  end
98
91
 
99
- def data
100
- result.data if result
101
- end
102
-
103
- def validates(*args, &block)
104
- result.validates(*args, &block) if result
105
- end
106
-
107
- def validate(*args, &block)
108
- result.validate(*args, &block) if result
109
- end
92
+ # support updates with dao-ish objects
93
+ #
94
+ add_conversion_method!(:to_dao)
95
+ add_conversion_method!(:as_dao)
110
96
 
111
- def valid?
112
- result.valid? if result
113
- end
114
-
115
- def validate!
116
- result.validate! if result
97
+ def update(*args, &block)
98
+ if args.size==1 and args.first.respond_to?(:to_dao)
99
+ to_dao = args.first.to_dao
100
+ return super(to_dao)
101
+ end
102
+ super
117
103
  end
118
104
  end
119
-
120
- def Dao.parse(*args, &block)
121
- Params.process(*args, &block)
122
- end
123
105
  end
@@ -3,7 +3,13 @@ module Dao
3
3
  class Error < ::StandardError; end
4
4
  class Error::Params < Error; end
5
5
 
6
+ # class methods
7
+ #
6
8
  class << Path
9
+ def default
10
+ Path.for(:dao)
11
+ end
12
+
7
13
  def for(*args)
8
14
  new(absolute_path_for(*args))
9
15
  end
@@ -21,15 +27,6 @@ module Dao
21
27
  ('/' + paths_for(arg, *args).join('/')).squeeze('/')
22
28
  end
23
29
 
24
- def cast(*args)
25
- if args.size == 1
26
- value = args.first
27
- value.is_a?(self) ? value : self.for(value)
28
- else
29
- self.for(*args)
30
- end
31
- end
32
-
33
30
  def params?(string)
34
31
  string.to_s =~ %r{/:[^/]+}
35
32
  end
@@ -9,7 +9,7 @@ Api =
9
9
 
10
10
 
11
11
 
12
- ## this is simply a suggest way to model your api. it is not required.
12
+ ## this is simply a suggested way to model your api. it is not required.
13
13
  #
14
14
  attr_accessor :effective_user
15
15
  attr_accessor :real_user
@@ -7,5 +7,21 @@ module DaoHelper
7
7
  result.error!
8
8
  end
9
9
  end
10
+
11
+ def dao(path, params, mode = nil)
12
+ unless mode
13
+ case request.method
14
+ when "GET"
15
+ mode = :read
16
+ when "PUT", "POST", "DELETE"
17
+ mode = :write
18
+ else
19
+ # do nothing - the user must specificy the mode explicity
20
+ end
21
+ end
22
+ result = api.send(mode, path, params)
23
+ result.route = request.fullpath
24
+ result
25
+ end
10
26
  end
11
27
  ApplicationController.send(:include, DaoHelper)
@@ -1,151 +1,44 @@
1
1
  module Dao
2
2
  class Result < ::Map
3
- include Dao::InstanceExec
4
-
5
- attr_accessor :api
6
- attr_accessor :interface
7
- attr_accessor :mode
8
- attr_accessor :params
9
- attr_accessor :validations
10
- attr_accessor :presenter
11
- attr_accessor :form
12
- attr_accessor :forcing_validity
13
-
14
- def Result.for(*args, &block)
15
- new(*args, &block)
16
- end
17
-
18
3
  def initialize(*args, &block)
19
4
  options = Dao.options_for!(args)
20
- args.push('/dao') if args.empty?
21
-
22
- path_args = args.select{|arg| arg.is_a?(String) or args.is_a?(Symbol)}
23
- data_args = args.select{|arg| arg.is_a?(Hash)}
24
- data_args += [options[:data]] if options.has_key?(:data)
25
-
26
- path = Path.for(*path_args)
27
- status = Status.ok
28
- errors = Errors.new
29
- data = Data.new
30
5
 
31
- data_args.each do |data_arg|
32
- data.update(data_arg)
33
- end
34
-
35
- api = options[:api]
36
- interface = options[:interface]
37
- params = options[:params] || Params.new
38
- mode = options[:mode] || (api ? api.mode : Mode.default)
6
+ self.path = args.shift || options[:path] || Path.default
7
+ self.route = options[:route] || Route.default
8
+ self.mode = options[:mode] || Mode.default
9
+ self.status = options[:status] || Status.default
10
+ self.errors = options[:errors] || Errors.new
11
+ self.params = options[:params] || Params.new
12
+ self.data = options[:data] || Data.new
39
13
 
40
14
  params.result = self
41
- path = interface.path if interface
42
-
43
- form = Form.for(self)
44
- validations = Validations.for(self)
45
- presenter = Presenter.for(self)
46
-
47
- self[:path] = path
48
- self[:status] = status
49
- self[:mode] = mode
50
- self[:errors] = errors
51
- self[:data] = data
52
-
53
- @api = api
54
- @interface = interface
55
- @params = params
56
- @form = form
57
- @validations = validations
58
- @presenter = presenter
59
- @forcing_validity = false
60
- end
61
-
62
- def path
63
- self[:path]
64
- end
65
-
66
- def status(*args)
67
- self[:status] = Status.for(*args) unless args.empty?
68
- self[:status]
69
- end
70
- def status=(value)
71
- status(value)
72
- end
73
-
74
- def mode(*args)
75
- self[:mode] = Mode.for(*args) unless args.empty?
76
- self[:mode]
77
- end
78
- def mode=(value)
79
- mode(value)
80
- end
81
-
82
- def errors
83
- self[:errors]
84
- end
85
-
86
- def data
87
- self[:data]
88
- end
89
-
90
- def is_valid=(boolean)
91
- @is_valid = !!boolean
92
- end
93
-
94
- def is_valid(*bool)
95
- @is_valid ||= nil
96
- @is_valid = !!bool.first unless bool.empty?
97
- @is_valid
98
- end
99
-
100
- def valid!
101
- @forcing_validity = true
102
- end
103
-
104
- def valid?(*args)
105
- if @forcing_validity
106
- true
107
- else
108
- options = Dao.options_for!(args)
109
- validate unless validations.ran?
110
- validate if options[:validate]
111
- errors.empty? and status.ok?
112
- end
113
- end
114
-
115
- def validate(*args, &block)
116
- if !args.empty?
117
- validations.add(*args, &block)
118
- else
119
- validations.run
120
- status(420) if(status.ok? and !errors.empty?)
121
- errors.empty? and status.ok?
122
- end
123
- end
124
-
125
- def validate!(*args, &block)
126
- if !args.empty?
127
- validations.add(*args, &block)
128
- end
129
- @forcing_validity = false
130
- validations.run!
131
- status(420) if(status.ok? and !errors.empty?)
132
- throw(:result, nil) unless(errors.empty? and status.ok?)
133
- end
134
-
135
- def validates(*args, &block)
136
- validations.add(*args, &block)
15
+ params.path = self.path
16
+ params.status = self.status
17
+ params.errors = self.errors
137
18
  end
138
19
 
139
20
  def error!
140
21
  raise Dao::Error::Result.for(self)
141
22
  end
142
23
 
143
- def tag(*args, &block)
144
- presenter.tag(*args, &block)
145
- end
146
-
24
+ # look good for inspect
25
+ #
147
26
  def inspect
148
27
  ::JSON.pretty_generate(self, :max_nesting => 0)
149
28
  end
29
+
30
+ # delegate some methods to the params
31
+ #
32
+ Validations::Mixin.list.each do |method|
33
+ module_eval <<-__, __FILE__, __LINE__
34
+ def #{ method }(*args)
35
+ params.send(#{ method.inspect }, *args)
36
+ end
37
+ __
38
+ end
39
+
40
+ def form
41
+ params.form
42
+ end
150
43
  end
151
44
  end
@@ -0,0 +1,87 @@
1
+ module Dao
2
+ class Route < ::String
3
+ Default = '/index'.freeze
4
+
5
+ class << Route
6
+ def default
7
+ Default
8
+ end
9
+
10
+ def like?(route)
11
+ route.to_s =~ %r{/:[^/]+}
12
+ end
13
+
14
+ def keys_for(route)
15
+ route = Path.absolute_path_for(route.to_s)
16
+ route.scan(%r{/:[^/]+}).map{|key| key.sub(%r{^/:}, '')}
17
+ end
18
+
19
+ def pattern_for(route)
20
+ route = Path.absolute_path_for(route.to_s)
21
+ re = route.gsub(%r{/:[^/]+}, '/([^/]+)')
22
+ /#{ re }/ioux
23
+ end
24
+
25
+ def path_for(route, params = {})
26
+ path = Path.absolute_path_for(route.to_s)
27
+ params = Map.new(params)
28
+ params.each do |key, val|
29
+ re = %r{/:#{ Regexp.escape(key.to_s) }(\Z|/)}
30
+ repl = "/#{ val.to_s }\\1"
31
+ path.gsub!(re, repl)
32
+ end
33
+ path
34
+ end
35
+ end
36
+
37
+ attr_accessor :keys
38
+ attr_accessor :pattern
39
+
40
+ def initialize(path)
41
+ replace(path.to_s)
42
+ @keys = Route.keys_for(self).freeze
43
+ @pattern = Route.pattern_for(self).freeze
44
+ freeze
45
+ end
46
+
47
+ def path
48
+ self
49
+ end
50
+
51
+ def path_for(params)
52
+ Route.path_for(self, params)
53
+ end
54
+
55
+ def match(path)
56
+ match = pattern.match(path).to_a
57
+ end
58
+
59
+ def params_for(path)
60
+ match = pattern.match(path).to_a
61
+ if match
62
+ map = Map.new
63
+ ignored = match.shift
64
+ @keys.each_with_index do |key, index|
65
+ map[key] = match[index]
66
+ end
67
+ map
68
+ end
69
+ end
70
+
71
+ class List < ::Array
72
+ def add(path)
73
+ route = Route.new(path)
74
+ push(route)
75
+ route
76
+ end
77
+
78
+ def match(name)
79
+ each do |route|
80
+ match = route.match(name)
81
+ return route if match
82
+ end
83
+ return nil
84
+ end
85
+ end
86
+ end
87
+ end
@@ -61,13 +61,91 @@ module Dao
61
61
  }
62
62
  ) unless defined?(Code2Message)
63
63
 
64
- def Status.underscore(camel_cased_word)
65
- camel_cased_word.to_s.gsub(/::/, '/').
66
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
67
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
68
- #gsub(/\s+/, '').
69
- tr("-", "_").
70
- downcase
64
+ Groups = ({
65
+ 100 => 'instruction',
66
+ 200 => 'success',
67
+ 300 => 'redirection',
68
+ 400 => 'client_error',
69
+ 500 => 'server_error'
70
+ }) unless defined?(Groups)
71
+
72
+ # class methods
73
+ #
74
+ class << Status
75
+ def list
76
+ @list ||= Symbol2Code.sort_by{|sym, code| code}.map{|sym, code| send(sym)}
77
+ end
78
+
79
+ def underscore(camel_cased_word)
80
+ camel_cased_word.to_s.gsub(/::/, '/').
81
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
82
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
83
+ #gsub(/\s+/, '').
84
+ tr("-", "_").
85
+ downcase
86
+ end
87
+
88
+ def default
89
+ Status.for(200)
90
+ end
91
+
92
+ def for(*args)
93
+ if args.size >= 2
94
+ code = args.shift
95
+ message = args.join(' ')
96
+ new(code, message)
97
+ else
98
+ arg = args.shift
99
+ case arg
100
+ when Result
101
+ result = arg
102
+ if arg.errors.nil? or arg.errors.empty? or arg.valid?
103
+ new(200)
104
+ else
105
+ new(500)
106
+ end
107
+ when Status
108
+ arg
109
+ when Fixnum
110
+ code = arg
111
+ message = Code2Message[code]
112
+ new(code, message)
113
+ when Symbol, String
114
+ if arg.to_s =~ %r/^\d+$/
115
+ code = arg.to_i
116
+ else
117
+ sym = Status.underscore(arg).to_sym
118
+ code = Symbol2Code[sym]
119
+ end
120
+ if code
121
+ message = Code2Message[code]
122
+ else
123
+ code = 500
124
+ message = "Unknown Status #{ arg }"
125
+ end
126
+ new(code, message)
127
+ else
128
+ if arg.respond_to?(:code) and arg.respond_to?(:message)
129
+ code, message = arg.code, arg.message
130
+ new(code, message)
131
+ else
132
+ parse(arg)
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def parse(string)
139
+ first, last = string.to_s.strip.split(%r/\s+/, 2)
140
+ if first =~ %r/^\d+$/
141
+ code = Integer(first)
142
+ message = last
143
+ else
144
+ code = 500
145
+ message = "Unknown Status #{ string.inspect }"
146
+ end
147
+ new(code, message)
148
+ end
71
149
  end
72
150
 
73
151
  Symbol2Code = (
@@ -80,16 +158,22 @@ module Dao
80
158
  Symbol2Code.each do |sym, code|
81
159
  module_eval <<-__
82
160
  def Status.#{ sym }()
83
- @#{ sym } ||= Status.for(:#{ sym })
161
+ @#{ sym } ||= Status.for(:#{ sym }).freeze
84
162
  end
85
163
  __
86
164
  end
87
165
 
166
+ # instance methods
167
+ #
88
168
  attr :code
89
169
  attr :message
90
170
  attr :group
91
171
 
92
172
  def initialize(*args)
173
+ update(*args)
174
+ end
175
+
176
+ def update(*args)
93
177
  code, message =
94
178
  if args.size == 2
95
179
  [args.first, args.last]
@@ -101,18 +185,10 @@ module Dao
101
185
  @group = (@code / 100) * 100
102
186
  replace("#{ @code } #{ @message }".strip)
103
187
  end
104
-
105
- Groups = ({
106
- 100 => 'instruction',
107
- 200 => 'success',
108
- 300 => 'redirection',
109
- 400 => 'client_error',
110
- 500 => 'server_error'
111
- }) unless defined?(Groups)
188
+ alias_method('set', 'update')
112
189
 
113
190
  Groups.each do |code, group|
114
191
  module_eval <<-__, __FILE__, __LINE__ -1
115
-
116
192
  def Status.#{ group }
117
193
  @status_group_#{ group } ||= Status.for(#{ code })
118
194
  end
@@ -120,23 +196,18 @@ module Dao
120
196
  def #{ group }?()
121
197
  #{ code } == @group
122
198
  end
123
-
124
199
  __
125
200
  end
126
201
 
127
- def Status.list
128
- @list ||= Symbol2Code.sort_by{|sym, code| code}.map{|sym, code| send(sym)}
129
- end
130
-
131
202
  def good?
132
203
  @group < 400
133
204
  end
134
- alias_method 'ok?', 'good?'
205
+ alias_method('ok?', 'good?')
135
206
 
136
207
  def bad?
137
208
  @group >= 400
138
209
  end
139
- alias_method 'error?', 'bad?'
210
+ alias_method('error?', 'bad?')
140
211
 
141
212
  def =~(other)
142
213
  begin
@@ -163,75 +234,6 @@ module Dao
163
234
  def to_json(*args, &block)
164
235
  Map[:code, code, :message, message].to_json(*args, &block)
165
236
  end
166
-
167
- class << Status
168
- def for(*args)
169
- if args.size >= 2
170
- code = args.shift
171
- message = args.join(' ')
172
- new(code, message)
173
- else
174
- arg = args.shift
175
- case arg
176
- when Result
177
- result = arg
178
- if arg.errors.nil? or arg.errors.empty? or arg.valid?
179
- new(200)
180
- else
181
- new(500)
182
- end
183
- when Status
184
- arg
185
- when Fixnum
186
- code = arg
187
- message = Code2Message[code]
188
- new(code, message)
189
- when Symbol, String
190
- if arg.to_s =~ %r/^\d+$/
191
- code = arg.to_i
192
- else
193
- sym = Status.underscore(arg).to_sym
194
- code = Symbol2Code[sym]
195
- end
196
- if code
197
- message = Code2Message[code]
198
- else
199
- code = 500
200
- message = "Unknown Status #{ arg }"
201
- end
202
- new(code, message)
203
- else
204
- if arg.respond_to?(:code) and arg.respond_to?(:message)
205
- code, message = arg.code, arg.message
206
- new(code, message)
207
- else
208
- parse(arg)
209
- end
210
- end
211
- end
212
- end
213
-
214
- def parse(string)
215
- first, last = string.to_s.strip.split(%r/\s+/, 2)
216
- if first =~ %r/^\d+$/
217
- code = Integer(first)
218
- message = last
219
- else
220
- code = 500
221
- message = "Unknown Status #{ string.inspect }"
222
- end
223
- new(code, message)
224
- end
225
-
226
- def cast(*args)
227
- if args.size == 1
228
- value = args.first
229
- value.is_a?(self) ? value : self.for(value)
230
- else
231
- self.for(*args)
232
- end
233
- end
234
- end
235
237
  end
236
238
 
237
239
  def Dao.status(*args, &block)
@@ -243,8 +245,8 @@ module Dao
243
245
  end
244
246
  end
245
247
 
246
- __END__
247
248
 
249
+ __END__
248
250
 
249
251
  ### ref: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
250
252