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
@@ -0,0 +1,71 @@
|
|
1
|
+
module Dao
|
2
|
+
class Api
|
3
|
+
Initializers = {} unless defined?(Initializers)
|
4
|
+
|
5
|
+
class << Api
|
6
|
+
def new(*args, &block)
|
7
|
+
allocate.instance_eval do
|
8
|
+
before_initializers(*args, &block)
|
9
|
+
initialize(*args, &block)
|
10
|
+
after_initializers(*args, &block)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initializers
|
16
|
+
Initializers[self] ||= {:before => [], :after => []}
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_initializers
|
20
|
+
initializers[:before]
|
21
|
+
end
|
22
|
+
|
23
|
+
def before_initializer(&block)
|
24
|
+
method_name = "before_initializer_#{ before_initializers.size }"
|
25
|
+
define_method(method_name, &block)
|
26
|
+
before_initializers.push(method_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_initializers
|
30
|
+
initializers[:after]
|
31
|
+
end
|
32
|
+
|
33
|
+
def after_initializer(&block)
|
34
|
+
after_initializers.push(block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def superclasses
|
38
|
+
@superclasses ||= ancestors.select{|ancestor| ancestor <= Dao::Api}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def superclasses
|
43
|
+
@superclasses ||= self.class.superclasses
|
44
|
+
end
|
45
|
+
|
46
|
+
def run_initializers(which, *args, &block)
|
47
|
+
superclasses.each do |superclass|
|
48
|
+
superclass.send("#{ which }_initializers").each do |method_name|
|
49
|
+
send(method_name, *args, &block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
send("#{ which }_initialize", *args, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def before_initializers(*args, &block)
|
56
|
+
run_initializers(:before, *args, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def after_initializers(*args, &block)
|
60
|
+
run_initializers(:after, *args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_initialize(*args, &block)
|
64
|
+
:hook
|
65
|
+
end
|
66
|
+
|
67
|
+
def after_initialize(*args, &block)
|
68
|
+
:hook
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
module Dao
|
3
|
+
class Api
|
4
|
+
class << Api
|
5
|
+
def modes(*modes)
|
6
|
+
@modes ||= []
|
7
|
+
modes.flatten.compact.map{|mode| Api.add_mode(mode)} unless modes.empty?
|
8
|
+
@modes
|
9
|
+
end
|
10
|
+
|
11
|
+
def modes=(*modes)
|
12
|
+
modes(*modes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_mode(mode)
|
16
|
+
modes.push(mode = Mode.for(mode)).uniq!
|
17
|
+
|
18
|
+
module_eval(<<-__, __FILE__, __LINE__ - 1)
|
19
|
+
|
20
|
+
def #{ mode }(*args, &block)
|
21
|
+
if args.empty?
|
22
|
+
mode(#{ mode.inspect }, &block)
|
23
|
+
else
|
24
|
+
mode(#{ mode.inspect }) do
|
25
|
+
call(*args, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def #{ mode }?(&block)
|
31
|
+
mode?(#{ mode.inspect }, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
__
|
35
|
+
|
36
|
+
mode
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def mode=(mode)
|
41
|
+
@mode = Mode.for(mode)
|
42
|
+
end
|
43
|
+
|
44
|
+
def mode(*args, &block)
|
45
|
+
@mode ||= Mode.default
|
46
|
+
|
47
|
+
if args.empty? and block.nil?
|
48
|
+
@mode
|
49
|
+
else
|
50
|
+
if block
|
51
|
+
mode = self.mode
|
52
|
+
self.mode = args.shift
|
53
|
+
begin
|
54
|
+
return(instance_eval(&block))
|
55
|
+
ensure
|
56
|
+
self.mode = mode
|
57
|
+
end
|
58
|
+
else
|
59
|
+
self.mode = args.shift
|
60
|
+
return(self)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def mode?(mode, &block)
|
66
|
+
condition = self.mode == mode
|
67
|
+
|
68
|
+
if block.nil?
|
69
|
+
condition
|
70
|
+
else
|
71
|
+
if condition
|
72
|
+
result = block.call
|
73
|
+
throw(:result, result) if catching_the_result?
|
74
|
+
result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Api.modes = Mode.list
|
81
|
+
|
82
|
+
Api.before_initializer do |*args|
|
83
|
+
@mode = Mode.default
|
84
|
+
end
|
85
|
+
end
|
data/lib/dao/data.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Dao
|
2
|
+
class Data < ::Map
|
3
|
+
add_conversion_method!(:to_dao)
|
4
|
+
add_conversion_method!(:as_dao)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
=begin
|
10
|
+
%w( to_dao as_dao ).each do |method|
|
11
|
+
module_eval <<-__, __FILE__, __LINE__
|
12
|
+
def #{ method }(object, *args, &block)
|
13
|
+
case object
|
14
|
+
when Array
|
15
|
+
object.map{|element| Data.#{ method }(element)}
|
16
|
+
|
17
|
+
else
|
18
|
+
if object.respond_to?(:#{ method })
|
19
|
+
object.send(:#{ method }, *args, &block)
|
20
|
+
else
|
21
|
+
object
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
__
|
26
|
+
end
|
27
|
+
=end
|
28
|
+
|
29
|
+
=begin
|
30
|
+
IdKeys =
|
31
|
+
%w( id uuid guid ).map{|key| [key, key.to_sym, "_#{ key }", "_#{ key }".to_sym]}.flatten
|
32
|
+
|
33
|
+
def id
|
34
|
+
IdKeys.each{|key| return self[key] if has_key?(key)}
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_id?
|
39
|
+
IdKeys.each{|key| return true if has_key?(key)}
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
def new?
|
44
|
+
!has_id?
|
45
|
+
end
|
46
|
+
|
47
|
+
def new_record?
|
48
|
+
!has_id?
|
49
|
+
end
|
50
|
+
|
51
|
+
def model_name
|
52
|
+
path.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
def slug
|
56
|
+
Slug.for(path)
|
57
|
+
end
|
58
|
+
=end
|
data/lib/dao/db.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'yaml/store'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Dao
|
6
|
+
class Db
|
7
|
+
attr_accessor :path
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
options = Dao.options_for!(args)
|
11
|
+
@path = (args.shift || options[:path] || './db/dao.yml').to_s
|
12
|
+
FileUtils.mkdir_p(File.dirname(@path)) rescue nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def db
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def ystore
|
20
|
+
@ystore ||= YAML::Store.new(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
class Collection
|
24
|
+
def initialize(name, db)
|
25
|
+
@name = name.to_s
|
26
|
+
@db = db
|
27
|
+
end
|
28
|
+
|
29
|
+
def save(data = {})
|
30
|
+
@db.save(@name, data)
|
31
|
+
end
|
32
|
+
alias_method(:create, :save)
|
33
|
+
alias_method(:update, :save)
|
34
|
+
|
35
|
+
def find(id = :all)
|
36
|
+
@db.find(@name, id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def all
|
40
|
+
find(:all)
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](id)
|
44
|
+
find(id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(id)
|
48
|
+
@db.delete(@name, id)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_hash
|
52
|
+
transaction{|y| y[@name]}
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_yaml(*args, &block)
|
56
|
+
Hash.new.update(to_hash).to_yaml(*args, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def transaction(*args, &block)
|
60
|
+
@db.ystore.transaction(*args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def [](name)
|
65
|
+
Collection.new(name, db)
|
66
|
+
end
|
67
|
+
|
68
|
+
def transaction(*args, &block)
|
69
|
+
ystore.transaction(*args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def save(collection, data = {})
|
73
|
+
data = data_for(data)
|
74
|
+
ystore.transaction do |y|
|
75
|
+
collection = (y[collection.to_s] ||= {})
|
76
|
+
id = next_id_for(collection, data)
|
77
|
+
collection[id] = data
|
78
|
+
record = collection[id]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def data_for(data)
|
83
|
+
data ? Map.for(data) : nil
|
84
|
+
end
|
85
|
+
|
86
|
+
alias_method(:create, :save)
|
87
|
+
|
88
|
+
def find(collection, id = :all, &block)
|
89
|
+
ystore.transaction do |y|
|
90
|
+
collection = (y[collection.to_s] ||= {})
|
91
|
+
if id.nil? or id == :all
|
92
|
+
list = collection.values.map{|data| data_for(data)}
|
93
|
+
if block
|
94
|
+
collection[:all] = list.map{|record| data_for(block.call(record))}
|
95
|
+
else
|
96
|
+
list
|
97
|
+
end
|
98
|
+
else
|
99
|
+
key = String(id)
|
100
|
+
record = data_for(collection[key])
|
101
|
+
if block
|
102
|
+
collection[key] = data_for(block.call(record))
|
103
|
+
else
|
104
|
+
record
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def update(collection, id = :all, updates = {})
|
111
|
+
data = data_for(data)
|
112
|
+
find(collection, id) do |record|
|
113
|
+
record.update(updates)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def delete(collection, id = :all)
|
118
|
+
ystore.transaction do |y|
|
119
|
+
collection = (y[collection.to_s] ||= {})
|
120
|
+
if id.nil? or id == :all
|
121
|
+
collection.clear()
|
122
|
+
else
|
123
|
+
deleted = collection.delete(String(id))
|
124
|
+
data_for(deleted) if deleted
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def next_id_for(collection, data)
|
130
|
+
data = data_for(data)
|
131
|
+
begin
|
132
|
+
id = id_for(data)
|
133
|
+
raise if id.strip.empty?
|
134
|
+
id
|
135
|
+
rescue
|
136
|
+
data['id'] = String(collection.size + 1)
|
137
|
+
id_for(data)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def id_for(data)
|
142
|
+
data = data_for(data)
|
143
|
+
%w( id _id ).each{|key| return String(data[key]) if data.has_key?(key)}
|
144
|
+
raise("no id discoverable for #{ data.inspect }")
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_hash
|
148
|
+
ystore.transaction do |y|
|
149
|
+
y.roots.inject(Hash.new){|h,k| h.update(k => y[k])}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_yaml(*args, &block)
|
154
|
+
to_hash.to_yaml(*args, &block)
|
155
|
+
end
|
156
|
+
|
157
|
+
class << Db
|
158
|
+
attr_writer :root
|
159
|
+
attr_writer :instance
|
160
|
+
|
161
|
+
def default_root()
|
162
|
+
defined?(Rails.root) ? File.join(Rails.root.to_s, 'db') : './db'
|
163
|
+
end
|
164
|
+
|
165
|
+
def default_path()
|
166
|
+
File.join(default_root, 'dao.yml')
|
167
|
+
end
|
168
|
+
|
169
|
+
def method_missing(method, *args, &block)
|
170
|
+
super unless instance.respond_to?(method)
|
171
|
+
instance.send(method, *args, &block)
|
172
|
+
end
|
173
|
+
|
174
|
+
def instance
|
175
|
+
@instance ||= Db.new(Db.default_path)
|
176
|
+
end
|
177
|
+
|
178
|
+
def root
|
179
|
+
@root ||= default_root
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/dao/endpoint.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Dao
|
2
|
+
class Endpoint
|
3
|
+
Attrs = %w( api path method doc )
|
4
|
+
Attrs.each{|attr| attr_accessor(attr)}
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
update(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update(options = {})
|
11
|
+
options.each do |key, val|
|
12
|
+
send("#{ key }=", val)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/dao/engine.rb
ADDED
data/lib/dao/errors.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
module Dao
|
2
|
+
class Errors < ::Map
|
3
|
+
Global = '*' unless defined?(Global)
|
4
|
+
Separator = ':' unless defined?(Separator)
|
5
|
+
|
6
|
+
class Message < ::String
|
7
|
+
attr_accessor :sticky
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
11
|
+
replace(args.join(' '))
|
12
|
+
@sticky = options[:sticky]
|
13
|
+
end
|
14
|
+
|
15
|
+
def sticky?
|
16
|
+
@sticky ||= nil
|
17
|
+
!!@sticky
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class << Errors
|
22
|
+
def global_key
|
23
|
+
[Global]
|
24
|
+
end
|
25
|
+
|
26
|
+
def for(*args, &block)
|
27
|
+
new(*args, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def cast(*args)
|
31
|
+
if args.size == 1
|
32
|
+
value = args.first
|
33
|
+
value.is_a?(self) ? value : self.for(value)
|
34
|
+
else
|
35
|
+
self.for(*args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add(*args)
|
41
|
+
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
42
|
+
sticky = options[:sticky]
|
43
|
+
clear = options[:clear]
|
44
|
+
|
45
|
+
args.flatten!
|
46
|
+
message = args.pop
|
47
|
+
keys = args
|
48
|
+
keys = [Global] if keys.empty?
|
49
|
+
errors = Hash.new
|
50
|
+
|
51
|
+
if Array(keys) == [Global]
|
52
|
+
sticky = true unless options.has_key?(:sticky)
|
53
|
+
end
|
54
|
+
|
55
|
+
sticky = true if(message.respond_to?(:sticky?) and message.sticky?)
|
56
|
+
|
57
|
+
if message
|
58
|
+
if message.respond_to?(:full_messages)
|
59
|
+
message.depth_first_each do |keys, msg|
|
60
|
+
errors[keys] = Message.new(msg, :sticky => sticky)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
errors[keys] = Message.new(message, :sticky => sticky)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
result = []
|
68
|
+
|
69
|
+
errors.each do |keys, message|
|
70
|
+
list = get(keys)
|
71
|
+
unless get(keys)
|
72
|
+
set(keys => [])
|
73
|
+
list = get(keys)
|
74
|
+
end
|
75
|
+
list.clear if clear
|
76
|
+
list.push(message)
|
77
|
+
result = list
|
78
|
+
end
|
79
|
+
|
80
|
+
result
|
81
|
+
end
|
82
|
+
alias_method 'add_to_base', 'add'
|
83
|
+
|
84
|
+
def clone
|
85
|
+
clone = Errors.new
|
86
|
+
depth_first_each do |keys, message|
|
87
|
+
args = [*keys]
|
88
|
+
args.push(message)
|
89
|
+
clone.add(*args)
|
90
|
+
end
|
91
|
+
clone
|
92
|
+
end
|
93
|
+
|
94
|
+
def add!(*args)
|
95
|
+
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
96
|
+
options[:sticky] = true
|
97
|
+
args.push(options)
|
98
|
+
add(*args)
|
99
|
+
end
|
100
|
+
alias_method 'add_to_base!', 'add!'
|
101
|
+
|
102
|
+
alias_method 'clear!', 'clear' unless instance_methods.include?('clear!')
|
103
|
+
|
104
|
+
def update(other, options = {})
|
105
|
+
options = Dao.map_for(options)
|
106
|
+
prefix = Array(options[:prefix]).flatten.compact
|
107
|
+
|
108
|
+
other.each do |key, val|
|
109
|
+
key = key.to_s
|
110
|
+
if key == 'base' or key == Global
|
111
|
+
add!(val)
|
112
|
+
else
|
113
|
+
key = prefix + [key] unless prefix.empty?
|
114
|
+
add(key, val)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def clear
|
120
|
+
keep = []
|
121
|
+
depth_first_each do |keys, message|
|
122
|
+
index = keys.pop
|
123
|
+
args = [keys, message].flatten
|
124
|
+
keep.push(args) if message.sticky?
|
125
|
+
end
|
126
|
+
clear!
|
127
|
+
ensure
|
128
|
+
keep.each{|args| add!(*args)}
|
129
|
+
end
|
130
|
+
|
131
|
+
def invalid?(*keys)
|
132
|
+
!get(keys).nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
alias_method 'on?', 'invalid?'
|
136
|
+
|
137
|
+
alias_method 'on', 'get'
|
138
|
+
|
139
|
+
def size
|
140
|
+
size = 0
|
141
|
+
depth_first_each{ size += 1 }
|
142
|
+
size
|
143
|
+
end
|
144
|
+
|
145
|
+
alias_method 'count', 'size'
|
146
|
+
alias_method 'length', 'size'
|
147
|
+
|
148
|
+
def full_messages
|
149
|
+
full_messages = []
|
150
|
+
|
151
|
+
depth_first_each do |keys, value|
|
152
|
+
index = keys.pop
|
153
|
+
key = keys.join('.')
|
154
|
+
value = value.to_s
|
155
|
+
next if value.strip.empty?
|
156
|
+
full_messages.push([key, value])
|
157
|
+
end
|
158
|
+
|
159
|
+
full_messages.sort! do |a,b|
|
160
|
+
a, b = a.first, b.first
|
161
|
+
if a == Global
|
162
|
+
b == Global ? 0 : -1
|
163
|
+
elsif b == Global
|
164
|
+
a == Global ? 0 : 1
|
165
|
+
else
|
166
|
+
a <=> b
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
full_messages
|
171
|
+
end
|
172
|
+
|
173
|
+
def each_message
|
174
|
+
depth_first_each do |keys, message|
|
175
|
+
index = keys.pop
|
176
|
+
message = message.to_s.strip
|
177
|
+
next if message.empty?
|
178
|
+
yield(keys, message)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def each_full_message
|
183
|
+
full_messages.each{|msg| yield msg}
|
184
|
+
end
|
185
|
+
|
186
|
+
alias_method 'each_full', 'each_full_message'
|
187
|
+
|
188
|
+
def messages
|
189
|
+
messages =
|
190
|
+
(self[Global]||[]).map{|message| message}.
|
191
|
+
select{|message| not message.strip.empty?}
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_html(*args)
|
195
|
+
Errors.to_html(errors=self, *args)
|
196
|
+
end
|
197
|
+
|
198
|
+
def Errors.to_html(*args, &block)
|
199
|
+
if block
|
200
|
+
define_method(:to_html, &block)
|
201
|
+
else
|
202
|
+
default_errors_to_html(*args)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def Errors.default_errors_to_html(*args)
|
207
|
+
error = args.shift
|
208
|
+
options = Dao.map_for(args.last.is_a?(Hash) ? args.pop : {})
|
209
|
+
errors = [error, *args].flatten.compact
|
210
|
+
|
211
|
+
at_least_one = false
|
212
|
+
css_class = options[:class] || 'dao errors'
|
213
|
+
|
214
|
+
html = []
|
215
|
+
|
216
|
+
html.push("<table class='#{ css_class }'>")
|
217
|
+
html.push("<caption>Sorry, there were some errors.</caption>")
|
218
|
+
errors.each do |e|
|
219
|
+
e.full_messages.each do |key, value|
|
220
|
+
at_least_one = true
|
221
|
+
key = key.to_s
|
222
|
+
html.push("<tr class='field'>")
|
223
|
+
html.push("<td class='field'>#{ key }</td>")
|
224
|
+
html.push("<td class='separator'>#{ Separator }</td>")
|
225
|
+
html.push("<td class='message'>#{ value }</td>")
|
226
|
+
html.push("</tr>")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
html.push("</table>")
|
230
|
+
|
231
|
+
at_least_one ? html.join("\n") : ''
|
232
|
+
end
|
233
|
+
|
234
|
+
def to_s(*args, &block)
|
235
|
+
to_html(*args, &block)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|