dao 4.2.1 → 4.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/README +103 -63
  2. data/Rakefile +3 -3
  3. data/dao.gemspec +27 -16
  4. data/lib/dao.rb +17 -17
  5. data/lib/dao/active_record.rb +1 -0
  6. data/lib/dao/api.rb +2 -1
  7. data/lib/dao/api/{endpoints.rb → call.rb} +1 -0
  8. data/lib/dao/api/context.rb +2 -0
  9. data/lib/dao/api/dsl.rb +1 -0
  10. data/lib/dao/api/initializers.rb +1 -0
  11. data/lib/dao/api/modes.rb +1 -0
  12. data/lib/dao/api/routes.rb +1 -0
  13. data/lib/dao/blankslate.rb +1 -0
  14. data/lib/dao/conducer.rb +315 -274
  15. data/lib/dao/conducer/active_model.rb +98 -0
  16. data/lib/dao/conducer/attributes.rb +1 -0
  17. data/lib/dao/conducer/autocrud.rb +58 -0
  18. data/lib/dao/conducer/callback_support.rb +20 -0
  19. data/lib/dao/conducer/collection.rb +45 -0
  20. data/lib/dao/conducer/controller_support.rb +104 -0
  21. data/lib/dao/conducer/nav_support.rb +9 -0
  22. data/lib/dao/conducer/view_support.rb +16 -0
  23. data/lib/dao/data.rb +2 -1
  24. data/lib/dao/db.rb +2 -0
  25. data/lib/dao/endpoint.rb +1 -0
  26. data/lib/dao/engine.rb +1 -0
  27. data/lib/dao/errors.rb +109 -99
  28. data/lib/dao/exceptions.rb +1 -0
  29. data/lib/dao/extractor.rb +1 -0
  30. data/lib/dao/form.rb +175 -20
  31. data/lib/dao/instance_exec.rb +1 -0
  32. data/lib/dao/mode.rb +1 -0
  33. data/lib/dao/mongo_mapper.rb +1 -0
  34. data/lib/dao/name.rb +1 -0
  35. data/lib/dao/params.rb +2 -1
  36. data/lib/dao/path.rb +1 -0
  37. data/lib/dao/path_map.rb +24 -0
  38. data/lib/dao/rack.rb +1 -0
  39. data/lib/dao/rack/middleware.rb +1 -0
  40. data/lib/dao/rack/middleware/params_parser.rb +1 -0
  41. data/lib/dao/rails.rb +12 -32
  42. data/lib/dao/rails/lib/generators/dao/USAGE +2 -2
  43. data/lib/dao/rails/lib/generators/dao/dao_generator.rb +8 -27
  44. data/lib/dao/rails/lib/generators/dao/templates/api.rb +2 -1
  45. data/lib/dao/rails/lib/generators/dao/templates/api_controller.rb +22 -20
  46. data/lib/dao/rails/lib/generators/dao/templates/conducer.rb +49 -43
  47. data/lib/dao/rails/lib/generators/dao/templates/dao.css +26 -25
  48. data/lib/dao/rails/lib/generators/dao/templates/dao.js +3 -0
  49. data/lib/dao/rails/lib/generators/dao/templates/dao_helper.rb +58 -45
  50. data/lib/dao/result.rb +50 -1
  51. data/lib/dao/route.rb +1 -0
  52. data/lib/dao/slug.rb +12 -36
  53. data/lib/dao/status.rb +91 -7
  54. data/lib/dao/stdext.rb +1 -0
  55. data/lib/dao/support.rb +90 -80
  56. data/lib/dao/upload.rb +396 -0
  57. data/lib/dao/validations.rb +23 -5
  58. data/lib/dao/validations/callback.rb +5 -0
  59. data/lib/dao/validations/common.rb +100 -3
  60. data/lib/dao/validations/instance.rb +17 -0
  61. data/lib/dao/validations/validator.rb +192 -91
  62. data/test/active_model_conducer_lint_test.rb +1 -0
  63. data/test/api_test.rb +15 -0
  64. data/test/conducer_test.rb +608 -90
  65. data/test/data/han-solo.jpg +0 -0
  66. data/test/form_test.rb +1 -0
  67. data/test/helper.rb +1 -0
  68. data/test/leak.rb +1 -0
  69. data/test/support_test.rb +4 -1
  70. data/test/testing.rb +1 -0
  71. data/test/validations_test.rb +176 -30
  72. metadata +120 -131
  73. data/b.rb +0 -38
  74. data/lib/dao/conducer/crud.rb +0 -70
  75. data/lib/dao/current.rb +0 -66
  76. data/lib/dao/image_cache.rb +0 -193
  77. data/lib/dao/rails/lib/generators/dao/templates/conducer_controller.rb +0 -79
  78. data/test/db.yml +0 -9
data/lib/dao/result.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  class Result < ::Map
3
4
  def initialize(*args, &block)
@@ -12,8 +13,56 @@ module Dao
12
13
  self.data = options[:data] || Data.new
13
14
  end
14
15
 
16
+ =begin
17
+ %w(
18
+ path
19
+ route
20
+ mode
21
+ status
22
+ params
23
+ errors
24
+ data
25
+ ).each do |attr|
26
+
27
+ module_eval <<-__, __FILE__, __LINE__
28
+ def #{ attr }(*value)
29
+ unless value.empty?
30
+ self["#{ attr }"] = value.first
31
+ end
32
+ self["#{ attr }"]
33
+ end
34
+
35
+ def #{ attr }=(value)
36
+ self["#{ attr }"] = value
37
+ end
38
+ __
39
+
40
+ end
41
+
42
+ def name
43
+ path
44
+ end
45
+
46
+ def attributes
47
+ params
48
+ end
49
+ =end
50
+
51
+
52
+ def form
53
+ @form ||= (
54
+ Form.new.tap do |f|
55
+ f.object = self
56
+ f.attributes = params
57
+ f.errors = errors
58
+ f.status = status
59
+ f.name = path
60
+ end
61
+ )
62
+ end
63
+
15
64
  def inspect
16
- ::JSON.pretty_generate(self, :max_nesting => 0)
65
+ Dao.json_for(self)
17
66
  end
18
67
  end
19
68
  end
data/lib/dao/route.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  class Route < ::String
3
4
  Default = '/index'.freeze
data/lib/dao/slug.rb CHANGED
@@ -1,40 +1,16 @@
1
- begin
2
- require 'rubygems'
3
- rescue LoadError
4
- end
5
-
6
- begin
7
- require 'unidecode'
8
- rescue LoadError
9
- end
10
-
11
- class Slug < ::String
12
- Slug::Version = '0.0.1'
13
-
14
- def Slug.version
15
- '0.0.1'
16
- end
17
-
18
- Join = '-'
1
+ # -*- encoding : utf-8 -*-
2
+ module Dao
3
+ class Slug < ::String
4
+ Join = '-'
19
5
 
20
- def Slug.for(*args)
21
- options = args.last.is_a?(Hash) ? args.pop : {}
22
- join = options[:join]||options['join']||Join
23
- string = args.flatten.compact.join(join)
24
- string = unidecode(string)
25
- words = string.to_s.scan(%r/\w+/)
26
- words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
27
- words.delete_if{|word| word.nil? or word.strip.empty?}
28
- new(words.join(join).downcase)
29
- end
30
-
31
- unless defined?(Unidecoder)
32
- def Slug.unidecode(string)
33
- string
34
- end
35
- else
36
- def Slug.unidecode(string)
37
- Unidecoder.decode(string)
6
+ def Slug.for(*args)
7
+ options = args.last.is_a?(Hash) ? args.pop : {}
8
+ join = options[:join]||options['join']||Join
9
+ string = args.flatten.compact.join(join)
10
+ words = string.to_s.scan(%r/\w+/)
11
+ words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
12
+ words.delete_if{|word| word.nil? or word.strip.empty?}
13
+ new(words.join(join).downcase)
38
14
  end
39
15
  end
40
16
  end
data/lib/dao/status.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  class Status < ::String
3
4
  ## http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
@@ -57,7 +58,84 @@ module Dao
57
58
  504 => "Gateway Timeout",
58
59
  505 => "HTTP Version Not Supported",
59
60
  507 => "Insufficient Storage",
60
- 510 => "Not Extended"
61
+ 510 => "Not Extended",
62
+
63
+
64
+ ### ref: https://github.com/joho/7XX-rfc
65
+
66
+ #70X => "Inexcusable",
67
+ 701 => "Meh",
68
+ 702 => "Emacs",
69
+
70
+ #71X => "Novelty Implementations",
71
+ 710 => "PHP",
72
+ 711 => "Convenience Store",
73
+ 719 => "I am not a teapot",
74
+
75
+ #72X => "Edge Cases",
76
+ 720 => "Unpossible",
77
+ 721 => "Known Unknowns",
78
+ 722 => "Unknown Unknowns",
79
+ 723 => "Tricky",
80
+ 724 => "This line should be unreachable",
81
+ 725 => "It works on my machine",
82
+ 726 => "It's a feature, not a bug",
83
+
84
+ #73X => "Fucking",
85
+ 731 => "Fucking Rubygems",
86
+ 732 => "Fucking Unicode",
87
+ 733 => "Fucking Deadlocks",
88
+ 734 => "Fucking Deferreds",
89
+ 735 => "Fucking IE",
90
+ 736 => "Fucking Race Conditions",
91
+ 737 => "FuckThreadsing",
92
+ 738 => "Fucking Bundler",
93
+ 739 => "Fucking Windows",
94
+
95
+ #74X => "Meme Driven",
96
+ 741 => "Compiling",
97
+ 742 => "A kitten dies",
98
+ 743 => "I thought I knew regular expressions",
99
+ 744 => "Y U NO write integration tests?",
100
+ 745 => "I don't always test my code, but when I do I do it in production",
101
+ 746 => "Missed Ballmer Peak",
102
+ 747 => "Motherfucking Snakes on the Motherfucking Plane",
103
+ 748 => "Confounded by Ponies",
104
+ 749 => "Reserved for Chuck Norris",
105
+
106
+ #75X => "Syntax Errors",
107
+ 750 => "Didn't bother to compile it",
108
+ 753 => "Syntax Error",
109
+
110
+ #76X => "Substance-Affected Developer",
111
+ 761 => "Hungover",
112
+ 762 => "Stoned",
113
+ 763 => "Under-Caffeinated",
114
+ 764 => "Over-Caffeinated",
115
+ 765 => "Railscamp",
116
+ 766 => "Sober",
117
+ 767 => "Drunk",
118
+
119
+ #77X => "Predictable Problems",
120
+ 771 => "Cached for too long",
121
+ 772 => "Not cached long enough",
122
+ 773 => "Not cached at all",
123
+ 774 => "Why was this cached?",
124
+ 776 => "Error on the Exception",
125
+ 777 => "Coincidence",
126
+ 778 => "Off By One Error",
127
+ 779 => "Off By Too Many To Count Error",
128
+
129
+ #78X => "Somebody Else's Problem",
130
+ 781 => "Operations",
131
+ 782 => "QA",
132
+ 783 => "It was a customer request, honestly",
133
+ 784 => "Management, obviously",
134
+ 785 => "TPS Cover Sheet not attached",
135
+
136
+ #79X => "Internet crashed",
137
+ 797 => "This is the last page of the Internet. Go back",
138
+ 799 => "End of the world"
61
139
  }
62
140
  ) unless defined?(Code2Message)
63
141
 
@@ -66,7 +144,8 @@ module Dao
66
144
  200 => 'success',
67
145
  300 => 'redirection',
68
146
  400 => 'client_error',
69
- 500 => 'server_error'
147
+ 500 => 'server_error',
148
+ 700 => 'developer_error'
70
149
  }) unless defined?(Groups)
71
150
 
72
151
  # class methods
@@ -78,10 +157,14 @@ module Dao
78
157
 
79
158
  def underscore(camel_cased_word)
80
159
  camel_cased_word.to_s.gsub(/::/, '/').
160
+ gsub(/\s+/, '_').
81
161
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
82
162
  gsub(/([a-z\d])([A-Z])/,'\1_\2').
83
- #gsub(/\s+/, '').
163
+ gsub(/[^A-Za-z0-9_]/, '_').
84
164
  tr("-", "_").
165
+ squeeze('_').
166
+ gsub(/^_+/, '').
167
+ gsub(/_+$/, '').
85
168
  downcase
86
169
  end
87
170
 
@@ -150,7 +233,7 @@ module Dao
150
233
 
151
234
  Symbol2Code = (
152
235
  Code2Message.inject(Hash.new) do |hash, (code, message)|
153
- sym = Status.underscore(message.gsub(/\s+/, "")).to_sym
236
+ sym = Status.underscore(message.gsub(/\s+/, "_")).to_sym
154
237
  hash.update(sym => code)
155
238
  end
156
239
  ) unless defined?(Symbol2Code)
@@ -165,9 +248,10 @@ module Dao
165
248
 
166
249
  # instance methods
167
250
  #
168
- attr :code
169
- attr :message
170
- attr :group
251
+ attr_accessor :code
252
+ attr_accessor :message
253
+ attr_accessor :group
254
+ attr_accessor :source
171
255
 
172
256
  def initialize(*args)
173
257
  update(*args)
data/lib/dao/stdext.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  class Array
2
3
  def to_dao(*args, &block)
3
4
  Dao.to_dao(self, *args, &block)
data/lib/dao/support.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Dao
2
3
  def map_for(*args, &block)
3
4
  Map.for(*args, &block)
@@ -66,42 +67,29 @@ module Dao
66
67
  Form.name_for(path, *keys)
67
68
  end
68
69
 
69
- def parse(*args, &block)
70
- Params.process(*args, &block)
71
- end
72
-
73
- def normalize_parameters(params)
74
- Params.normalize_parameters(params)
75
- end
76
-
77
70
  def current
78
- @current ||=
79
- Map.new(
80
- :controller => nil
81
- )
71
+ Current
82
72
  end
83
73
 
84
74
  def current_controller(*args)
85
- current.controller = args.first unless args.empty?
86
- current.controller || mock_controller
75
+ Current.controller = args.first unless args.empty?
76
+ Current.controller
87
77
  end
88
78
  alias_method('controller', 'current_controller')
89
79
 
90
80
  def current_controller=(controller)
91
- current.controller = controller
81
+ Current.controller = controller
92
82
  end
93
83
  alias_method('controller=', 'current_controller=')
94
84
 
85
+ def mock_controller
86
+ Current.mock_controller
87
+ end
88
+
95
89
  %w( request response session ).each do |attr|
96
90
  module_eval <<-__, __FILE__, __LINE__
97
91
  def current_#{ attr }
98
- @current_#{ attr } ||= current_controller.instance_eval{ #{ attr } }
99
- end
100
- def current_#{ attr }=(value)
101
- @current_#{ attr } = value
102
- end
103
- def #{ attr }
104
- current_#{ attr }
92
+ current_controller.instance_eval{ #{ attr } }
105
93
  end
106
94
  __
107
95
  end
@@ -109,7 +97,7 @@ module Dao
109
97
  %w( current_user effective_user real_user ).each do |attr|
110
98
  module_eval <<-__, __FILE__, __LINE__
111
99
  def #{ attr }
112
- @#{ attr } ||= current_controller.instance_eval{ #{ attr } }
100
+ current_controller.instance_eval{ #{ attr } }
113
101
  end
114
102
  __
115
103
  end
@@ -122,58 +110,13 @@ module Dao
122
110
  end
123
111
  end
124
112
 
125
- def key_for(*keys)
126
- key = keys.flatten.join('.').strip
127
- key.split(%r/\s*[,.:_-]\s*/).map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
128
- end
129
-
130
- def mock_controller
131
- ensure_rails_application do
132
- require 'action_dispatch/testing/test_request.rb'
133
- require 'action_dispatch/testing/test_response.rb'
134
- store = ActiveSupport::Cache::MemoryStore.new
135
- controller =
136
- begin
137
- ApplicationController.new
138
- rescue NameError
139
- ActionController::Base.new
140
- end
141
- controller.perform_caching = true
142
- controller.cache_store = store
143
- request = ActionDispatch::TestRequest.new
144
- response = ActionDispatch::TestResponse.new
145
- controller.request = request
146
- controller.response = response
147
- controller.send(:initialize_template_class, response)
148
- controller.send(:assign_shortcuts, request, response)
149
- controller.send(:default_url_options).merge!(DefaultUrlOptions) if defined?(DefaultUrlOptions)
150
- controller
151
- end
152
- end
153
-
154
- def ensure_rails_application(&block)
155
- if Rails.application.nil?
156
- mock = Class.new(Rails::Application)
157
- Rails.application = mock.instance
158
- begin
159
- block.call()
160
- ensure
161
- Rails.application = nil
162
- end
163
- else
164
- block.call()
165
- end
166
- end
167
-
168
113
  def normalize_parameters(params)
169
114
  dao = (params.delete('dao') || {}).merge(params.delete(:dao) || {})
170
115
 
171
- unless dao.blank?
116
+ unless dao.empty?
172
117
  dao.each do |key, paths_and_values|
173
- params[key] = nil
174
118
  next if paths_and_values.blank?
175
-
176
- map = Map.new
119
+ map = Map.for(params[key])
177
120
 
178
121
  paths_and_values.each do |path, value|
179
122
  keys = keys_for(path)
@@ -182,16 +125,25 @@ module Dao
182
125
 
183
126
  params[key] = map
184
127
  end
128
+
129
+ params['dao'] = dao
185
130
  end
186
131
 
187
- #params[:dao] = {:normalized => true}
188
132
  params
189
133
  end
190
134
 
191
- def keys_for(keys)
192
- keys.strip.split(%r/\s*[,._-]\s*/).map{|key| key =~ %r/^\d+$/ ? Integer(key) : key}
135
+ def keys_for(*keys)
136
+ keys = keys.join('.').scan(/[^\,\.\s]+/iomx)
137
+
138
+ keys.map do |key|
139
+ digity, stringy, digits = %r/^(~)?(\d+)$/iomx.match(key).to_a
140
+
141
+ digity ? stringy ? String(digits) : Integer(digits) : key
142
+ end
193
143
  end
194
144
 
145
+ alias_method(:key_for, :keys_for)
146
+
195
147
  def render_json(object, options = {})
196
148
  options = options.to_options!
197
149
  controller = options[:controller] || Dao.current_controller
@@ -210,18 +162,76 @@ module Dao
210
162
  end
211
163
  end
212
164
 
213
- def json_for(object)
165
+ def json_for(object, options = {})
214
166
  object = object.as_json if object.respond_to?(:as_json)
215
167
 
168
+ options = options.empty? ? Map.for(options) : options
169
+ options[:pretty] = json_pretty? unless options.has_key?(:pretty)
170
+
216
171
  begin
217
- if Rails.env.production?
218
- ::JSON.generate(object)
219
- else
220
- ::JSON.pretty_generate(object, :max_nesting => 0)
221
- end
172
+ MultiJson.dump(object, options)
222
173
  rescue Object => e
223
- Rails.logger.error(e)
224
174
  YAML.load( object.to_yaml ).to_json
225
175
  end
226
176
  end
177
+
178
+ def json_pretty?
179
+ @json_pretty ||= (defined?(Rails) ? !Rails.env.production? : true)
180
+ end
181
+
182
+ def call(object, method, *args, &block)
183
+ args = Dao.args_for_arity(args, object.method(method).arity)
184
+ object.send(method, *args, &block)
185
+ end
186
+
187
+ def args_for_arity(args, arity)
188
+ arity = Integer(arity.respond_to?(:arity) ? arity.arity : arity)
189
+ arity < 0 ? args.dup : args.slice(0, arity)
190
+ end
191
+
192
+ def tree_walk(node, *path, &block)
193
+ iterator = Array === node ? :each_with_index : :each
194
+
195
+ node.send(iterator) do |key, val|
196
+ key, val = val, key if Array === node
197
+ path.push(key)
198
+ begin
199
+ caught =
200
+ catch(:tree_walk) do
201
+ block.call(path, val)
202
+ nil
203
+ end
204
+ next if caught==:next_sibling
205
+
206
+ case val
207
+ when Hash, Array
208
+ tree_walk(val, *path, &block)
209
+ end
210
+ ensure
211
+ path.pop
212
+ end
213
+ end
214
+ end
215
+
216
+
217
+ {
218
+ 'ffi-uuid' => proc{|*args| FFI::UUID.generate_time.to_s},
219
+ 'uuidtools' => proc{|*args| UUIDTools::UUID.timestamp_create.to_s},
220
+ 'uuid' => proc{|*args| UUID.generate.to_s},
221
+ }.each do |lib, implementation|
222
+ begin
223
+ require(lib)
224
+ define_method(:uuid, &implementation)
225
+ break
226
+ rescue LoadError
227
+ nil
228
+ end
229
+ end
230
+ abort 'no suitable uuid generation library detected' unless method_defined?(:uuid)
231
+
232
+ def ensure_interface!(object, *interface)
233
+ interface.flatten.compact.each do |method|
234
+ raise(NotImplementedError, "#{ object.class.name }##{ method }") unless object.respond_to?(method)
235
+ end
236
+ end
227
237
  end