moon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +11 -0
  3. data/Rakefile +48 -0
  4. data/lib/moon.rb +15 -0
  5. data/lib/moon/action.rb +12 -0
  6. data/lib/moon/action/base.rb +16 -0
  7. data/lib/moon/action/model.rb +10 -0
  8. data/lib/moon/action/model/create.rb +45 -0
  9. data/lib/moon/action/model/destroy.rb +42 -0
  10. data/lib/moon/action/model/index.rb +31 -0
  11. data/lib/moon/action/model/show.rb +32 -0
  12. data/lib/moon/action/model/update.rb +42 -0
  13. data/lib/moon/action/models.rb +8 -0
  14. data/lib/moon/action/models/finder.rb +43 -0
  15. data/lib/moon/action/models/initializer.rb +51 -0
  16. data/lib/moon/action/models/updater.rb +36 -0
  17. data/lib/moon/action/rebuild_arrays.rb +40 -0
  18. data/lib/moon/action/reference_object.rb +35 -0
  19. data/lib/moon/action/valid_models_required.rb +21 -0
  20. data/lib/moon/application.rb +33 -0
  21. data/lib/moon/application/rack.rb +104 -0
  22. data/lib/moon/application/routes.rb +16 -0
  23. data/lib/moon/context.rb +25 -0
  24. data/lib/moon/formatter.rb +22 -0
  25. data/lib/moon/formatter/base.rb +15 -0
  26. data/lib/moon/formatter/generic.rb +29 -0
  27. data/lib/moon/response.rb +7 -0
  28. data/lib/moon/response/base.rb +18 -0
  29. data/lib/moon/response/json.rb +26 -0
  30. data/lib/moon/response/json/collection.rb +12 -0
  31. data/lib/moon/response/json/message.rb +18 -0
  32. data/lib/moon/response/json/model.rb +16 -0
  33. data/lib/moon/response/json/validation_errors.rb +9 -0
  34. data/lib/moon/utility.rb +7 -0
  35. data/lib/moon/utility/string.rb +17 -0
  36. data/lib/moon/utility/template.rb +23 -0
  37. data/lib/moon/validator.rb +71 -0
  38. data/lib/moon/validator/format.rb +32 -0
  39. data/lib/moon/validator/length.rb +42 -0
  40. data/lib/moon/validator/presence.rb +29 -0
  41. data/spec/lib/moon/action/base_spec.rb +29 -0
  42. data/spec/lib/moon/action/model/create_spec.rb +111 -0
  43. data/spec/lib/moon/action/model/destroy_spec.rb +84 -0
  44. data/spec/lib/moon/action/model/index_spec.rb +51 -0
  45. data/spec/lib/moon/action/model/show_spec.rb +51 -0
  46. data/spec/lib/moon/action/model/update_spec.rb +91 -0
  47. data/spec/lib/moon/action/models/finder_spec.rb +36 -0
  48. data/spec/lib/moon/action/models/initializer_spec.rb +43 -0
  49. data/spec/lib/moon/action/models/updater_spec.rb +36 -0
  50. data/spec/lib/moon/action/rebuild_arrays_spec.rb +28 -0
  51. data/spec/lib/moon/action/reference_object_spec.rb +32 -0
  52. data/spec/lib/moon/action/valid_models_required_spec.rb +64 -0
  53. data/spec/lib/moon/application/rack_spec.rb +73 -0
  54. data/spec/lib/moon/application/routes_spec.rb +26 -0
  55. data/spec/lib/moon/context_spec.rb +28 -0
  56. data/spec/lib/moon/formatter/base_spec.rb +30 -0
  57. data/spec/lib/moon/formatter/generic_spec.rb +34 -0
  58. data/spec/lib/moon/formatter_spec.rb +36 -0
  59. data/spec/lib/moon/response/base_spec.rb +33 -0
  60. data/spec/lib/moon/response/json/collection_spec.rb +30 -0
  61. data/spec/lib/moon/response/json/message_spec.rb +30 -0
  62. data/spec/lib/moon/response/json/model_spec.rb +31 -0
  63. data/spec/lib/moon/response/json/validation_errors_spec.rb +21 -0
  64. data/spec/lib/moon/response/json_spec.rb +33 -0
  65. data/spec/lib/moon/utility/string_spec.rb +29 -0
  66. data/spec/lib/moon/utility/template_spec.rb +49 -0
  67. data/spec/lib/moon/validator/format_spec.rb +40 -0
  68. data/spec/lib/moon/validator/length_spec.rb +60 -0
  69. data/spec/lib/moon/validator/presence_spec.rb +45 -0
  70. data/spec/lib/moon/validator_spec.rb +83 -0
  71. data/spec/spec_helper.rb +4 -0
  72. 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
@@ -0,0 +1,7 @@
1
+
2
+ module Moon::Utility
3
+
4
+ autoload :String, File.join(File.dirname(__FILE__), "utility", "string")
5
+ autoload :Template, File.join(File.dirname(__FILE__), "utility", "template")
6
+
7
+ end
@@ -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