moon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +11 -0
- data/Rakefile +48 -0
- data/lib/moon.rb +15 -0
- data/lib/moon/action.rb +12 -0
- data/lib/moon/action/base.rb +16 -0
- data/lib/moon/action/model.rb +10 -0
- data/lib/moon/action/model/create.rb +45 -0
- data/lib/moon/action/model/destroy.rb +42 -0
- data/lib/moon/action/model/index.rb +31 -0
- data/lib/moon/action/model/show.rb +32 -0
- data/lib/moon/action/model/update.rb +42 -0
- data/lib/moon/action/models.rb +8 -0
- data/lib/moon/action/models/finder.rb +43 -0
- data/lib/moon/action/models/initializer.rb +51 -0
- data/lib/moon/action/models/updater.rb +36 -0
- data/lib/moon/action/rebuild_arrays.rb +40 -0
- data/lib/moon/action/reference_object.rb +35 -0
- data/lib/moon/action/valid_models_required.rb +21 -0
- data/lib/moon/application.rb +33 -0
- data/lib/moon/application/rack.rb +104 -0
- data/lib/moon/application/routes.rb +16 -0
- data/lib/moon/context.rb +25 -0
- data/lib/moon/formatter.rb +22 -0
- data/lib/moon/formatter/base.rb +15 -0
- data/lib/moon/formatter/generic.rb +29 -0
- data/lib/moon/response.rb +7 -0
- data/lib/moon/response/base.rb +18 -0
- data/lib/moon/response/json.rb +26 -0
- data/lib/moon/response/json/collection.rb +12 -0
- data/lib/moon/response/json/message.rb +18 -0
- data/lib/moon/response/json/model.rb +16 -0
- data/lib/moon/response/json/validation_errors.rb +9 -0
- data/lib/moon/utility.rb +7 -0
- data/lib/moon/utility/string.rb +17 -0
- data/lib/moon/utility/template.rb +23 -0
- data/lib/moon/validator.rb +71 -0
- data/lib/moon/validator/format.rb +32 -0
- data/lib/moon/validator/length.rb +42 -0
- data/lib/moon/validator/presence.rb +29 -0
- data/spec/lib/moon/action/base_spec.rb +29 -0
- data/spec/lib/moon/action/model/create_spec.rb +111 -0
- data/spec/lib/moon/action/model/destroy_spec.rb +84 -0
- data/spec/lib/moon/action/model/index_spec.rb +51 -0
- data/spec/lib/moon/action/model/show_spec.rb +51 -0
- data/spec/lib/moon/action/model/update_spec.rb +91 -0
- data/spec/lib/moon/action/models/finder_spec.rb +36 -0
- data/spec/lib/moon/action/models/initializer_spec.rb +43 -0
- data/spec/lib/moon/action/models/updater_spec.rb +36 -0
- data/spec/lib/moon/action/rebuild_arrays_spec.rb +28 -0
- data/spec/lib/moon/action/reference_object_spec.rb +32 -0
- data/spec/lib/moon/action/valid_models_required_spec.rb +64 -0
- data/spec/lib/moon/application/rack_spec.rb +73 -0
- data/spec/lib/moon/application/routes_spec.rb +26 -0
- data/spec/lib/moon/context_spec.rb +28 -0
- data/spec/lib/moon/formatter/base_spec.rb +30 -0
- data/spec/lib/moon/formatter/generic_spec.rb +34 -0
- data/spec/lib/moon/formatter_spec.rb +36 -0
- data/spec/lib/moon/response/base_spec.rb +33 -0
- data/spec/lib/moon/response/json/collection_spec.rb +30 -0
- data/spec/lib/moon/response/json/message_spec.rb +30 -0
- data/spec/lib/moon/response/json/model_spec.rb +31 -0
- data/spec/lib/moon/response/json/validation_errors_spec.rb +21 -0
- data/spec/lib/moon/response/json_spec.rb +33 -0
- data/spec/lib/moon/utility/string_spec.rb +29 -0
- data/spec/lib/moon/utility/template_spec.rb +49 -0
- data/spec/lib/moon/validator/format_spec.rb +40 -0
- data/spec/lib/moon/validator/length_spec.rb +60 -0
- data/spec/lib/moon/validator/presence_spec.rb +45 -0
- data/spec/lib/moon/validator_spec.rb +83 -0
- data/spec/spec_helper.rb +4 -0
- metadata +209 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
# Moon::Response::JSON::Collection provides a response that contains one single collection.
|
3
|
+
class Moon::Response::JSON::Collection < Moon::Response::JSON
|
4
|
+
|
5
|
+
def initialize(collection)
|
6
|
+
@collection = collection.map do |object|
|
7
|
+
Moon::Formatter.hash_for object
|
8
|
+
end
|
9
|
+
super 200, @collection
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
# Moon::Response::JSON::Message provides a simple response with one message
|
3
|
+
class Moon::Response::JSON::Message < Moon::Response::JSON
|
4
|
+
|
5
|
+
attr_accessor :message
|
6
|
+
attr_accessor :id
|
7
|
+
|
8
|
+
def initialize(status, message, id = nil)
|
9
|
+
@message, @id = message, id
|
10
|
+
super status
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
id = self.id
|
15
|
+
{ :message => message }.merge(id ? { :id => id } : { })
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
# Moon::Response::JSON::Collection provides a response that contains one single collection.
|
3
|
+
class Moon::Response::JSON::Model < Moon::Response::JSON
|
4
|
+
|
5
|
+
def initialize(key, object)
|
6
|
+
@key, @object = key, object
|
7
|
+
super 200, hash
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def hash
|
13
|
+
{ @key.to_s => Moon::Formatter.hash_for(@object) }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
|
2
|
+
# Moon::Response::JSON::ValidationErrors provides a simple response with the validation messages
|
3
|
+
class Moon::Response::JSON::ValidationErrors < Moon::Response::JSON
|
4
|
+
|
5
|
+
def initialize(validation_errors)
|
6
|
+
super 200, :validation_errors => validation_errors
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
data/lib/moon/utility.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module Moon::Utility::String
|
3
|
+
|
4
|
+
def self.camelize(lower_case_and_underscored_word)
|
5
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1}".upcase }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.underscore(camel_cased_word)
|
9
|
+
camel_cased_word.to_s.
|
10
|
+
gsub(/::/, '/').
|
11
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
12
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
13
|
+
tr("-", "_").
|
14
|
+
downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Moon::Utility::Template
|
3
|
+
|
4
|
+
def self.extended(base)
|
5
|
+
base.class_eval do
|
6
|
+
|
7
|
+
def self.template(*class_attributes)
|
8
|
+
@class_attributes = class_attributes
|
9
|
+
self.class.send :attr_accessor, *@class_attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[](*values)
|
13
|
+
klass = self.dup
|
14
|
+
(@class_attributes || [ ]).each do |class_attribute|
|
15
|
+
klass.send :"#{class_attribute}=", values.shift
|
16
|
+
end
|
17
|
+
klass
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
|
2
|
+
# Generic validator class. Can be used to validate model attributes.
|
3
|
+
class Moon::Validator
|
4
|
+
|
5
|
+
autoload :Format, File.join(File.dirname(__FILE__), "validator", "format")
|
6
|
+
autoload :Length, File.join(File.dirname(__FILE__), "validator", "length")
|
7
|
+
autoload :Presence, File.join(File.dirname(__FILE__), "validator", "presence")
|
8
|
+
|
9
|
+
attr_reader :model
|
10
|
+
|
11
|
+
def initialize(model)
|
12
|
+
@model = model
|
13
|
+
end
|
14
|
+
|
15
|
+
def ok?
|
16
|
+
perform_checks
|
17
|
+
@ok
|
18
|
+
end
|
19
|
+
|
20
|
+
def messages
|
21
|
+
perform_checks
|
22
|
+
@messages
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def perform_checks
|
28
|
+
@ok, @messages = true, { }
|
29
|
+
self.class.checks.each do |attribute, attribute_validator_class|
|
30
|
+
perform_attribute_checks attribute, attribute_validator_class
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def perform_attribute_checks(attribute, attribute_validator_classes)
|
35
|
+
attribute_validator_classes.each do |attribute_validator_class|
|
36
|
+
attribute_validator = attribute_validator_class.new @model.send(attribute)
|
37
|
+
ok, message = attribute_validator.ok?, attribute_validator.message
|
38
|
+
@ok &&= ok
|
39
|
+
add_message attribute, ok, message
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_message(attribute, ok, message)
|
44
|
+
return if ok || !message
|
45
|
+
@messages[attribute] ||= [ ]
|
46
|
+
@messages[attribute] << message
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.configuration=(value)
|
50
|
+
@configuration = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.configuration
|
54
|
+
@configuration
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.checks=(value)
|
58
|
+
@checks = value
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.checks
|
62
|
+
@checks || { }
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.[](model_class)
|
66
|
+
validator = self.dup
|
67
|
+
validator.checks = self.configuration[model_class]
|
68
|
+
validator
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
# Value format validator.
|
3
|
+
class Moon::Validator::Format
|
4
|
+
extend Moon::Utility::Template
|
5
|
+
|
6
|
+
template :format
|
7
|
+
|
8
|
+
attr_accessor :value
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
@value = value
|
12
|
+
@ok, @message = true, nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def ok?
|
16
|
+
check
|
17
|
+
@ok
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
check
|
22
|
+
@message
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def check
|
28
|
+
@ok = @value =~ self.class.format
|
29
|
+
@message = "Has a wrong format." unless @ok
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
# Value length validator.
|
3
|
+
class Moon::Validator::Length
|
4
|
+
extend Moon::Utility::Template
|
5
|
+
|
6
|
+
template :minimum, :maximum
|
7
|
+
|
8
|
+
attr_accessor :value
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
@value = value
|
12
|
+
@ok, @messages = true, [ ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def ok?
|
16
|
+
check
|
17
|
+
@ok
|
18
|
+
end
|
19
|
+
|
20
|
+
def message
|
21
|
+
check
|
22
|
+
@message
|
23
|
+
end
|
24
|
+
|
25
|
+
def minimum
|
26
|
+
self.class.minimum
|
27
|
+
end
|
28
|
+
|
29
|
+
def maximum
|
30
|
+
self.class.maximum
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def check
|
36
|
+
raise ArgumentError, "Value doesn't response to :size" unless @value.respond_to?(:size)
|
37
|
+
size, minimum, maximum = @value.size, self.minimum, self.maximum
|
38
|
+
@ok = (!minimum || size >= minimum) && (!maximum || size <= maximum)
|
39
|
+
@message = "Must have at #{size < minimum ? ("least " + minimum.to_s) : ("most " + maximum.to_s)} entries." unless @ok
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
# Value presence validator.
|
3
|
+
class Moon::Validator::Presence
|
4
|
+
|
5
|
+
attr_accessor :value
|
6
|
+
|
7
|
+
def initialize(value)
|
8
|
+
@value = value
|
9
|
+
@ok, @message = true, nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def ok?
|
13
|
+
check
|
14
|
+
@ok
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
check
|
19
|
+
@message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def check
|
25
|
+
@ok = @value && @value != ""
|
26
|
+
@message = "Must be present." unless @ok
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe Moon::Action::Base do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@context = mock Moon::Context
|
7
|
+
|
8
|
+
@action = described_class.new @context
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "context" do
|
12
|
+
|
13
|
+
it "should return the context" do
|
14
|
+
@action.context.should == @context
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "perform" do
|
20
|
+
|
21
|
+
it "should raise a #{NotImplementedError}" do
|
22
|
+
lambda do
|
23
|
+
@action.perform
|
24
|
+
end.should raise_error(NotImplementedError)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe Moon::Action::Model::Create do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@model_class = mock Class, :to_s => "Model"
|
7
|
+
@model = mock Object, :class => @model_class
|
8
|
+
|
9
|
+
@context = Moon::Context.new
|
10
|
+
@context.storage_name = :dump
|
11
|
+
@context.models[:model] = @model
|
12
|
+
|
13
|
+
described_class.model_class = @model_class
|
14
|
+
@action = described_class.new @context
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "perform" do
|
18
|
+
|
19
|
+
before :each do
|
20
|
+
GOM::Storage.stub :store
|
21
|
+
GOM::Object.stub :id => "test:object_1"
|
22
|
+
|
23
|
+
@model_response = mock Moon::Response::JSON::Model
|
24
|
+
Moon::Response::JSON::Model.stub :new => @model_response
|
25
|
+
|
26
|
+
@message_response = mock Moon::Response::JSON::Message
|
27
|
+
Moon::Response::JSON::Message.stub :new => @message_response
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should store the model on the right storage" do
|
31
|
+
GOM::Storage.should_receive(:store).with(@model, :dump)
|
32
|
+
@action.perform
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should initialize #{Moon::Response::JSON::Model}" do
|
36
|
+
Moon::Response::JSON::Model.should_receive(:new).with(:model, @model).and_return(@model_response)
|
37
|
+
@action.perform
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return the model response" do
|
41
|
+
response = @action.perform
|
42
|
+
response.should == @model_response
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should do nothing if the model isn't initialized" do
|
46
|
+
@context.models[:model] = nil
|
47
|
+
GOM::Storage.should_not_receive(:store)
|
48
|
+
@action.perform
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should initialize the 'failed' message response if the model isn't initialized" do
|
52
|
+
@context.models[:model] = nil
|
53
|
+
Moon::Response::JSON::Message.should_receive(:new).with(200, "No model initialized.").and_return(@message_response)
|
54
|
+
@action.perform
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return 'failed' message response if the model isn't initialized" do
|
58
|
+
@context.models[:model] = nil
|
59
|
+
response = @action.perform
|
60
|
+
response.should == @message_response
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "model" do
|
66
|
+
|
67
|
+
it "should return the model" do
|
68
|
+
model = @action.model
|
69
|
+
model.should == @model
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "self.model_symbol" do
|
75
|
+
|
76
|
+
it "should return the model symbol" do
|
77
|
+
model_symbol = described_class.model_symbol
|
78
|
+
model_symbol.should == :model
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "self.model_class" do
|
84
|
+
|
85
|
+
it "should return the model class" do
|
86
|
+
model_class = described_class.model_class
|
87
|
+
model_class.should == @model_class
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "self.[]" do
|
93
|
+
|
94
|
+
it "should return a class" do
|
95
|
+
result = described_class[Object]
|
96
|
+
result.should be_instance_of(Class)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should return a different class" do
|
100
|
+
result = described_class[Object]
|
101
|
+
result.should_not == described_class
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return a class connected to the given model class" do
|
105
|
+
result = described_class[Object]
|
106
|
+
result.model_class.should == Object
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "spec_helper"))
|
2
|
+
|
3
|
+
describe Moon::Action::Model::Destroy do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@model = mock Object
|
7
|
+
|
8
|
+
@context = Moon::Context.new
|
9
|
+
@context.models[:model] = @model
|
10
|
+
|
11
|
+
described_class.model_symbol = :model
|
12
|
+
@action = described_class.new @context
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "perform" do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
GOM::Storage.stub :remove => nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should rmeove the model" do
|
22
|
+
GOM::Storage.should_receive(:remove).with(@model)
|
23
|
+
@action.perform
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return 'ok' message response" do
|
27
|
+
response = @action.perform
|
28
|
+
response.should be_instance_of(Moon::Response::JSON::Message)
|
29
|
+
response.status.should == 200
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should do nothing if the model isn't initialized" do
|
33
|
+
@context.models[:model] = nil
|
34
|
+
GOM::Storage.should_not_receive(:remove)
|
35
|
+
@action.perform
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return 'failed' message response if model isn't initialized" do
|
39
|
+
@context.models[:model] = nil
|
40
|
+
response = @action.perform
|
41
|
+
response.should be_instance_of(Moon::Response::JSON::Message)
|
42
|
+
response.status.should == 401
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "model" do
|
48
|
+
|
49
|
+
it "should return the model" do
|
50
|
+
model = @action.model
|
51
|
+
model.should == @model
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "self.model_symbol" do
|
57
|
+
|
58
|
+
it "should return the model symbol" do
|
59
|
+
model_symbol = described_class.model_symbol
|
60
|
+
model_symbol.should == :model
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "self.[]" do
|
66
|
+
|
67
|
+
it "should return a class" do
|
68
|
+
result = described_class[:model]
|
69
|
+
result.should be_instance_of(Class)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return a different class" do
|
73
|
+
result = described_class[:model]
|
74
|
+
result.should_not == described_class
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return a class connected to the given model symbol" do
|
78
|
+
result = described_class[:model]
|
79
|
+
result.model_symbol.should == :model
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|