splash 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +19 -0
- data/lib/splash/acts_as_collection.rb +55 -0
- data/lib/splash/acts_as_scope.rb +118 -0
- data/lib/splash/acts_as_scope_root.rb +10 -0
- data/lib/splash/annotated.rb +34 -0
- data/lib/splash/application.rb +36 -0
- data/lib/splash/attribute.rb +45 -0
- data/lib/splash/attributed_struct.rb +8 -0
- data/lib/splash/collection.rb +52 -0
- data/lib/splash/connection.rb +26 -0
- data/lib/splash/constraint/all.rb +24 -0
- data/lib/splash/constraint/any.rb +13 -0
- data/lib/splash/constraint/in.rb +13 -0
- data/lib/splash/constraint/not_nil.rb +9 -0
- data/lib/splash/constraint.rb +62 -0
- data/lib/splash/document.rb +38 -0
- data/lib/splash/embed.rb +37 -0
- data/lib/splash/has_attributes.rb +146 -0
- data/lib/splash/has_constraint.rb +9 -0
- data/lib/splash/namespace.rb +67 -0
- data/lib/splash/password.rb +31 -0
- data/lib/splash/persister.rb +66 -0
- data/lib/splash/query_interface.rb +84 -0
- data/lib/splash/saveable.rb +141 -0
- data/lib/splash/scope/options.rb +58 -0
- data/lib/splash/scope.rb +46 -0
- data/lib/splash/scope_delegator.rb +14 -0
- data/lib/splash/scope_options.rb +31 -0
- data/lib/splash/standart_extensions/hash.rb +45 -0
- data/lib/splash/standart_extensions/module.rb +73 -0
- data/lib/splash/standart_extensions/object.rb +11 -0
- data/lib/splash/standart_extensions/string.rb +5 -0
- data/lib/splash/validates.rb +75 -0
- data/lib/splash/validator.rb +24 -0
- data/lib/splash.rb +35 -0
- data/spec/helper.rb +15 -0
- data/spec/lib/annotated_spec.rb +87 -0
- data/spec/lib/collection_spec.rb +64 -0
- data/spec/lib/has_attributes_spec.rb +43 -0
- data/spec/lib/has_constraints_spec.rb +53 -0
- data/spec/lib/inheritance_spec.rb +69 -0
- metadata +139 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.join File.dirname(__FILE__), "object"
|
2
|
+
require File.join File.dirname(__FILE__), "module"
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
DEEP_MERGER=proc{|key,value,value2|
|
6
|
+
if( value.kind_of?(Hash) && value2.kind_of?(Hash) )
|
7
|
+
value.merge(value2,&DEEP_MERGER)
|
8
|
+
else
|
9
|
+
value2
|
10
|
+
end
|
11
|
+
}
|
12
|
+
|
13
|
+
def deep_merge(k)
|
14
|
+
return self if k.nil?
|
15
|
+
merge(k,&DEEP_MERGER)
|
16
|
+
end
|
17
|
+
|
18
|
+
def deep_merge!(k)
|
19
|
+
return self if k.nil?
|
20
|
+
merge!(k,&DEEP_MERGER)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_saveable
|
24
|
+
return self.inject({}) do |h,(k,v)|
|
25
|
+
h[k]=Splash::Saveable.to_saveable(v);
|
26
|
+
h
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def only(keys)
|
31
|
+
self.reject{|key,val| !keys.include? key}
|
32
|
+
end
|
33
|
+
|
34
|
+
def hashmap
|
35
|
+
self.inject({}) do |newhash, (k,v)|
|
36
|
+
newhash[k] = yield(k, v)
|
37
|
+
newhash
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def +(hsh)
|
42
|
+
self.merge(hsh)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
def persister
|
4
|
+
@persister ||= Splash::Persister
|
5
|
+
end
|
6
|
+
|
7
|
+
def persister=(p)
|
8
|
+
@persister=p
|
9
|
+
end
|
10
|
+
|
11
|
+
def named?
|
12
|
+
to_s[0..1]!="#<"
|
13
|
+
end
|
14
|
+
|
15
|
+
def define_annotation(name)
|
16
|
+
self.class_eval <<-DEF,__FILE__, __LINE__
|
17
|
+
alias_method #{(name.to_s+"!").to_sym.inspect}, #{name.inspect}
|
18
|
+
def #{name.to_s}(*args,&block)
|
19
|
+
@annotations ||= []
|
20
|
+
@annotations << [#{(name.to_s+"!").to_sym.inspect},args,block]
|
21
|
+
end
|
22
|
+
DEF
|
23
|
+
end
|
24
|
+
|
25
|
+
def merged_inheritable_attr(name,default=[],&block)
|
26
|
+
if block_given?
|
27
|
+
merger = lambda &block
|
28
|
+
else
|
29
|
+
merger = lambda{|a,b| a if b.nil?; a + b }
|
30
|
+
end
|
31
|
+
|
32
|
+
self.instance_eval do
|
33
|
+
|
34
|
+
@merged_inheritable_attr_info ||={}
|
35
|
+
|
36
|
+
@merged_inheritable_attr_info[name] = {:default=>default,:merger => merger }
|
37
|
+
|
38
|
+
def self.merged_inheritable_attr_info(name)
|
39
|
+
if @merged_inheritable_attr_info && @merged_inheritable_attr_info.key?(name)
|
40
|
+
return @merged_inheritable_attr_info[name]
|
41
|
+
end
|
42
|
+
superclass.merged_inheritable_attr_info(name) if superclass.respond_to?(:merged_inheritable_attr_info)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
self.instance_eval <<-DEF,__FILE__, __LINE__
|
50
|
+
def #{name.to_s}
|
51
|
+
@#{name.to_s} ||= merged_inheritable_attr_info(#{name.inspect})[:default].dup
|
52
|
+
end
|
53
|
+
def #{name.to_s}=(v)
|
54
|
+
@#{name.to_s}=v
|
55
|
+
end
|
56
|
+
def all_#{name.to_s}
|
57
|
+
unless superclass.respond_to? :all_#{name.to_s}
|
58
|
+
return #{name.to_s}
|
59
|
+
end
|
60
|
+
merged_inheritable_attr_info(#{name.inspect})[:merger].call(#{name.to_s},superclass.all_#{name.to_s})
|
61
|
+
end
|
62
|
+
def each_#{name.to_s}
|
63
|
+
c=self
|
64
|
+
begin
|
65
|
+
yield(c.#{name.to_s})
|
66
|
+
c=c.superclass
|
67
|
+
end while(c.respond_to? #{name.to_sym.inspect})
|
68
|
+
end
|
69
|
+
|
70
|
+
DEF
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "set"
|
2
|
+
module Splash::Validates
|
3
|
+
|
4
|
+
class Invalid < Exception
|
5
|
+
|
6
|
+
attr_reader :object, :validators
|
7
|
+
|
8
|
+
def initialize(obj)
|
9
|
+
super
|
10
|
+
self.object = obj
|
11
|
+
self.validators = Set.new
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def included(base)
|
18
|
+
base.instance_eval do
|
19
|
+
merged_inheritable_attr :validators,Set.new
|
20
|
+
end
|
21
|
+
base.extend(ClassMethods)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def validate(*args,&block)
|
27
|
+
if args.first.respond_to? :valid?
|
28
|
+
validators << validator
|
29
|
+
end
|
30
|
+
validators << Splash::Validator.new(*args,&block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid?
|
35
|
+
validators = self.class.all_validators
|
36
|
+
|
37
|
+
validators.sort! do |a,b|
|
38
|
+
if b.depends.include? a.field
|
39
|
+
-1
|
40
|
+
elsif a.depends.include? b.field
|
41
|
+
1
|
42
|
+
else
|
43
|
+
0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
return validators.all? do |validator|
|
48
|
+
validator.valid?(self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def raise_if_invalid
|
53
|
+
v = Invalid.new(self)
|
54
|
+
|
55
|
+
validators = self.class.all_validators
|
56
|
+
|
57
|
+
validators.sort! do |a,b|
|
58
|
+
if b.depends.include? a.field
|
59
|
+
-1
|
60
|
+
elsif a.depends.include? b.field
|
61
|
+
1
|
62
|
+
else
|
63
|
+
0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
validators.each do |validator|
|
68
|
+
v.validators << validator unless validator.valid?(self)
|
69
|
+
end
|
70
|
+
|
71
|
+
raise v if v.validators.size > 0
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Splash::Validator
|
2
|
+
|
3
|
+
attr_reader :field,:block,:description,:depends
|
4
|
+
|
5
|
+
def valid?(object)
|
6
|
+
if @block
|
7
|
+
return @block.call(object)
|
8
|
+
else
|
9
|
+
value = object.send(@field)
|
10
|
+
if value.respond_to? :valid?
|
11
|
+
return value.valid?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(field, description,options={},&block)
|
18
|
+
@field = field
|
19
|
+
@block = block
|
20
|
+
@description = description
|
21
|
+
@depends = (options[:depends] || Set.new)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/lib/splash.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
if defined? Splash
|
2
|
+
raise "Splash included twice!"
|
3
|
+
end
|
4
|
+
|
5
|
+
module Splash
|
6
|
+
|
7
|
+
DIR = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
autoload :ActsAsCollection,DIR+"/splash/acts_as_collection"
|
10
|
+
autoload :ActsAsScope,DIR+"/splash/acts_as_scope"
|
11
|
+
autoload :ActsAsScopeRoot,DIR+"/splash/acts_as_scope_root"
|
12
|
+
autoload :Annotated,DIR+"/splash/annotated"
|
13
|
+
autoload :Scope,DIR+"/splash/scope"
|
14
|
+
autoload :Entity,DIR+"/splash/entity"
|
15
|
+
autoload :Validates,DIR+"/splash/validates"
|
16
|
+
autoload :Saveable,DIR+"/splash/saveable"
|
17
|
+
autoload :HasAttributes,DIR+"/splash/has_attributes"
|
18
|
+
autoload :NameSpace,DIR+"/splash/namespace"
|
19
|
+
autoload :Persister, DIR+"/splash/persister"
|
20
|
+
autoload :Lazy,DIR+"/splash/lazy"
|
21
|
+
autoload :QueryInterface,DIR+"/splash/query_interface"
|
22
|
+
autoload :Embed,DIR+"/splash/embed"
|
23
|
+
autoload :Document,DIR+"/splash/document"
|
24
|
+
autoload :Collection,DIR+"/splash/collection"
|
25
|
+
autoload :ScopeDelegator,DIR+"/splash/scope_delegator"
|
26
|
+
autoload :Attribute, DIR+"/splash/attribute"
|
27
|
+
autoload :HasConstraint, DIR+"/splash/has_constraint"
|
28
|
+
autoload :Constraint, DIR+"/splash/constraint"
|
29
|
+
autoload :AttributedStruct, DIR+"/splash/attributed_struct"
|
30
|
+
autoload :Password, DIR+"/splash/password"
|
31
|
+
|
32
|
+
end
|
33
|
+
Dir[Splash::DIR+"/splash/standart_extensions/*.rb"].each do |path|
|
34
|
+
require path
|
35
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
gem "mongo"
|
3
|
+
require "mongo"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__),"../../Humanized/lib/humanized")
|
6
|
+
|
7
|
+
culture=Humanized::Culture.new
|
8
|
+
culture.default_case = :nominativ
|
9
|
+
culture.converter = Humanized::Converter.new({})
|
10
|
+
|
11
|
+
|
12
|
+
Humanized::Culture.native=culture
|
13
|
+
Humanized::Culture.current=culture
|
14
|
+
|
15
|
+
require File.join(File.dirname(__FILE__),"../lib/splash")
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"../helper")
|
2
|
+
|
3
|
+
describe Splash::Annotated do
|
4
|
+
|
5
|
+
it "should work for modules" do
|
6
|
+
|
7
|
+
module NameAnnotations
|
8
|
+
|
9
|
+
include Splash::Annotated
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def included(base)
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
def named(fn,name)
|
21
|
+
@named||={}
|
22
|
+
@named[fn]=name
|
23
|
+
end
|
24
|
+
|
25
|
+
def name(fn)
|
26
|
+
return @named[fn] rescue nil
|
27
|
+
end
|
28
|
+
|
29
|
+
define_annotation :named
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class ClassWithSimpleAnnotations
|
35
|
+
|
36
|
+
include NameAnnotations
|
37
|
+
|
38
|
+
named "Nice Function!"
|
39
|
+
def nice
|
40
|
+
return 42
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
ClassWithSimpleAnnotations.name(:nice).should == "Nice Function!"
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should work for classes" do
|
50
|
+
|
51
|
+
class AnnotatedParent
|
52
|
+
|
53
|
+
include Splash::Annotated
|
54
|
+
|
55
|
+
class << self
|
56
|
+
|
57
|
+
def named(fn,name)
|
58
|
+
@named||={}
|
59
|
+
@named[fn]=name
|
60
|
+
end
|
61
|
+
|
62
|
+
def name(fn)
|
63
|
+
return @named[fn] rescue nil
|
64
|
+
end
|
65
|
+
|
66
|
+
define_annotation :named
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class Child < AnnotatedParent
|
73
|
+
|
74
|
+
include NameAnnotations
|
75
|
+
|
76
|
+
named "Nice Function!"
|
77
|
+
def nice
|
78
|
+
return 42
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
Child.name(:nice).should == "Nice Function!"
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"../helper")
|
2
|
+
|
3
|
+
describe Splash::ActsAsCollection do
|
4
|
+
|
5
|
+
it "should support class integration" do
|
6
|
+
|
7
|
+
class Friend
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class FriendList < Array
|
12
|
+
|
13
|
+
include Splash::ActsAsCollection.of(Friend)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
FriendList.should < Splash::ActsAsCollection
|
18
|
+
|
19
|
+
fl = FriendList.new
|
20
|
+
|
21
|
+
fl.respond_to?(:create).should be_true
|
22
|
+
|
23
|
+
fl.create
|
24
|
+
|
25
|
+
fl.should have(1).item
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should support the collection class" do
|
30
|
+
|
31
|
+
class Friend
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
fl = Splash::Collection.of(Friend).new
|
36
|
+
|
37
|
+
fl.respond_to?(:create).should be_true
|
38
|
+
|
39
|
+
fl.create
|
40
|
+
|
41
|
+
fl.should have(1).item
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should support comparison" do
|
46
|
+
|
47
|
+
|
48
|
+
class User
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class Admin < User
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
fl = Splash::Collection.of(User).new
|
57
|
+
|
58
|
+
fl.should be_a(Splash::Collection.of(User))
|
59
|
+
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"../helper")
|
2
|
+
|
3
|
+
describe Splash::HasAttributes do
|
4
|
+
|
5
|
+
describe "declaration" do
|
6
|
+
|
7
|
+
it "should look cool and work" do
|
8
|
+
|
9
|
+
class User
|
10
|
+
|
11
|
+
include Splash::HasAttributes
|
12
|
+
|
13
|
+
|
14
|
+
# simple style
|
15
|
+
def_attribute 'name'
|
16
|
+
|
17
|
+
# simple with type
|
18
|
+
def_attribute 'friends', Splash::Collection.of(User)
|
19
|
+
|
20
|
+
def_attribute( 'mails', Splash::Collection.of(String)) do
|
21
|
+
|
22
|
+
self.default = Splash::Collection.of(String).new
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
User.attributes.should have(3).items
|
29
|
+
|
30
|
+
u = User.new
|
31
|
+
|
32
|
+
u.name.should be_nil
|
33
|
+
|
34
|
+
u.friends.should be_nil
|
35
|
+
|
36
|
+
u.mails.should_not be_nil
|
37
|
+
u.mails.should be_a(Splash::Collection.of(String))
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"../helper")
|
2
|
+
|
3
|
+
describe Splash::HasConstraint do
|
4
|
+
|
5
|
+
describe "declaration" do
|
6
|
+
|
7
|
+
it "should work in a trivial case" do
|
8
|
+
|
9
|
+
class Field
|
10
|
+
|
11
|
+
include Splash::HasConstraint
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
f = Field.new
|
16
|
+
|
17
|
+
f.constraint.should_not be_nil
|
18
|
+
|
19
|
+
f.constraint.accept?(nil).should be_true
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should give the power to write simple constraints fast" do
|
24
|
+
|
25
|
+
class Field
|
26
|
+
|
27
|
+
include Splash::HasConstraint
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
f = Field.new
|
32
|
+
|
33
|
+
f.constraint.create "should not be nil" do |value|
|
34
|
+
!value.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
f.constraint.create "should be a string with 5 to 10 chars" do |value|
|
38
|
+
value.kind_of?(String) && (5..10).include?(value.length)
|
39
|
+
end
|
40
|
+
|
41
|
+
f.constraint.should have(2).items
|
42
|
+
|
43
|
+
f.constraint.accept?(nil).should be_false
|
44
|
+
|
45
|
+
f.constraint.accept?("aaa").should be_false
|
46
|
+
|
47
|
+
f.constraint.accept?("aaaaa").should be_true
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__),"../helper")
|
2
|
+
|
3
|
+
describe "inheritance" do
|
4
|
+
|
5
|
+
it "should generate normal methods" do
|
6
|
+
|
7
|
+
class A
|
8
|
+
|
9
|
+
merged_inheritable_attr :a
|
10
|
+
merged_inheritable_attr :b,"b"
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
A.respond_to?(:a).should be_true
|
15
|
+
A.respond_to?(:b).should be_true
|
16
|
+
|
17
|
+
A.b.should == "b"
|
18
|
+
|
19
|
+
A.all_b.should == "b"
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should generate inheritable methods" do
|
25
|
+
|
26
|
+
class A
|
27
|
+
|
28
|
+
merged_inheritable_attr :a
|
29
|
+
merged_inheritable_attr :b,"b" do |a,b|
|
30
|
+
a + b
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class B < A
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class C < B
|
40
|
+
merged_inheritable_attr :c
|
41
|
+
end
|
42
|
+
|
43
|
+
A.a << 1
|
44
|
+
|
45
|
+
B.respond_to?(:a).should be_true
|
46
|
+
B.respond_to?(:b).should be_true
|
47
|
+
|
48
|
+
B.b = "c"
|
49
|
+
|
50
|
+
B.a << 2
|
51
|
+
|
52
|
+
B.all_b.should == "cb"
|
53
|
+
|
54
|
+
C.all_b.should == "bcb"
|
55
|
+
|
56
|
+
C.a << 3
|
57
|
+
|
58
|
+
A.a.should have(1).item
|
59
|
+
A.all_a.should have(1).item
|
60
|
+
|
61
|
+
B.a.should have(1).item
|
62
|
+
B.all_a.should have(2).items
|
63
|
+
|
64
|
+
C.a.should have(1).item
|
65
|
+
C.all_a.should have(3).items
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|