dao 2.2.3 → 3.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.
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