moon 0.0.1 → 0.0.2

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.
Files changed (67) hide show
  1. data/README.rdoc +2 -0
  2. data/Rakefile +10 -3
  3. data/lib/moon.rb +0 -2
  4. data/lib/moon/action.rb +4 -3
  5. data/lib/moon/action/inject_timestamps.rb +44 -0
  6. data/lib/moon/action/model.rb +1 -2
  7. data/lib/moon/action/model/destroy.rb +4 -26
  8. data/lib/moon/action/model/index.rb +10 -17
  9. data/lib/moon/action/model/show.rb +9 -23
  10. data/lib/moon/action/model/store.rb +20 -0
  11. data/lib/moon/action/models/finder.rb +14 -14
  12. data/lib/moon/action/models/initializer.rb +6 -5
  13. data/lib/moon/action/models/updater.rb +1 -4
  14. data/lib/moon/action/rebuild_arrays.rb +1 -4
  15. data/lib/moon/action/{reference_object.rb → reference.rb} +1 -1
  16. data/lib/moon/action/respond_blank.rb +9 -0
  17. data/lib/moon/action/transfer.rb +30 -0
  18. data/lib/moon/action/valid_models_required.rb +13 -6
  19. data/lib/moon/application.rb +12 -7
  20. data/lib/moon/application/configuration.rb +55 -0
  21. data/lib/moon/application/rack.rb +17 -7
  22. data/lib/moon/context.rb +1 -1
  23. data/lib/moon/formatter.rb +4 -13
  24. data/lib/moon/formatter/generic.rb +17 -4
  25. data/lib/moon/response/json.rb +1 -0
  26. data/lib/moon/response/json/blank.rb +9 -0
  27. data/lib/moon/response/json/collection.rb +2 -2
  28. data/lib/moon/response/json/model.rb +3 -3
  29. data/lib/moon/validator.rb +17 -45
  30. data/lib/moon/validator/format.rb +4 -23
  31. data/lib/moon/validator/length.rb +8 -33
  32. data/lib/moon/validator/presence.rb +4 -17
  33. data/spec/lib/moon/action/inject_timestamps_spec.rb +46 -0
  34. data/spec/lib/moon/action/model/destroy_spec.rb +6 -44
  35. data/spec/lib/moon/action/model/index_spec.rb +8 -25
  36. data/spec/lib/moon/action/model/show_spec.rb +33 -25
  37. data/spec/lib/moon/action/model/store_spec.rb +77 -0
  38. data/spec/lib/moon/action/models/finder_spec.rb +25 -11
  39. data/spec/lib/moon/action/models/initializer_spec.rb +7 -5
  40. data/spec/lib/moon/action/models/updater_spec.rb +5 -3
  41. data/spec/lib/moon/action/rebuild_arrays_spec.rb +3 -3
  42. data/spec/lib/moon/action/{reference_object_spec.rb → reference_spec.rb} +1 -1
  43. data/spec/lib/moon/action/respond_blank_spec.rb +24 -0
  44. data/spec/lib/moon/action/transfer_spec.rb +30 -0
  45. data/spec/lib/moon/action/valid_models_required_spec.rb +19 -17
  46. data/spec/lib/moon/application/configuration_spec.rb +75 -0
  47. data/spec/lib/moon/application/rack_spec.rb +6 -5
  48. data/spec/lib/moon/formatter/generic_spec.rb +13 -3
  49. data/spec/lib/moon/formatter_spec.rb +4 -10
  50. data/spec/lib/moon/response/json/blank_spec.rb +19 -0
  51. data/spec/lib/moon/response/json/collection_spec.rb +5 -4
  52. data/spec/lib/moon/response/json/model_spec.rb +5 -4
  53. data/spec/lib/moon/validator/format_spec.rb +8 -25
  54. data/spec/lib/moon/validator/length_spec.rb +10 -36
  55. data/spec/lib/moon/validator/presence_spec.rb +11 -28
  56. data/spec/lib/moon/validator_spec.rb +10 -59
  57. metadata +34 -28
  58. data/lib/moon/action/base.rb +0 -16
  59. data/lib/moon/action/model/create.rb +0 -45
  60. data/lib/moon/action/model/update.rb +0 -42
  61. data/lib/moon/application/routes.rb +0 -16
  62. data/lib/moon/formatter/base.rb +0 -15
  63. data/spec/lib/moon/action/base_spec.rb +0 -29
  64. data/spec/lib/moon/action/model/create_spec.rb +0 -111
  65. data/spec/lib/moon/action/model/update_spec.rb +0 -91
  66. data/spec/lib/moon/application/routes_spec.rb +0 -26
  67. data/spec/lib/moon/formatter/base_spec.rb +0 -30
@@ -2,8 +2,8 @@
2
2
  # Main application class.
3
3
  class Moon::Application
4
4
 
5
+ autoload :Configuration, File.join(File.dirname(__FILE__), "application", "configuration")
5
6
  autoload :Rack, File.join(File.dirname(__FILE__), "application", "rack")
6
- autoload :Routes, File.join(File.dirname(__FILE__), "application", "routes")
7
7
 
8
8
  # This error is raised if an action doesn't respond to :perform.
9
9
  class InvalidActionError < StandardError; end
@@ -11,23 +11,28 @@ class Moon::Application
11
11
  # This error is raised if a response doesn't respond to :status, :headers and :body.
12
12
  class InvalidResponseError < StandardError; end
13
13
 
14
- attr_reader :routes
15
- attr_reader :rack
14
+ attr_reader :configuration
16
15
 
17
16
  def initialize
18
- @routes = Routes.new
17
+ @rack = Rack.new self
18
+ end
19
+
20
+ def configure(&block)
21
+ @configuration = Configuration.new &block
22
+ @rack.session = @configuration.session
23
+ @rack.routes = @configuration.routes
19
24
  end
20
25
 
21
26
  def rack
22
- @routes.rack.transponder
27
+ @rack.transponder
23
28
  end
24
29
 
25
30
  def environment
26
31
  @rack.environment
27
32
  end
28
33
 
29
- def self.storage_name(environment)
30
- environment == :test ? :dump : :main
34
+ def storage_name
35
+ @rack.environment == :test ? :dump : :main
31
36
  end
32
37
 
33
38
  end
@@ -0,0 +1,55 @@
1
+ require 'configure'
2
+
3
+ # Application configuration.
4
+ class Moon::Application::Configuration
5
+
6
+ SCHEMA = Configure::Schema.build {
7
+ only :session, :route, :validator, :formatter
8
+ nested {
9
+ session {
10
+ only :secret
11
+ }
12
+ route {
13
+ only :http_method, :path, :actions
14
+ argument_keys :http_method, :path
15
+ }
16
+ validator {
17
+ only :model_class, :attributes
18
+ }
19
+ formatter {
20
+ only :model_class, :object
21
+ }
22
+ }
23
+ }.freeze
24
+
25
+ def initialize(&block)
26
+ @hash = Configure.process SCHEMA, &block
27
+ end
28
+
29
+ def session
30
+ @hash[:session]
31
+ end
32
+
33
+ def routes
34
+ [ @hash[:route] ].compact.flatten
35
+ end
36
+
37
+ def validators
38
+ validators = { }
39
+ [ @hash[:validator] ].compact.flatten.each do |validator_hash|
40
+ model_class, attributes = validator_hash.values_at :model_class, :attributes
41
+ validators[model_class] = attributes
42
+ end
43
+ validators
44
+ end
45
+
46
+ def formatters
47
+ formatters = { }
48
+ [ @hash[:formatter] ].compact.flatten.each do |formatter_hash|
49
+ model_class, object = formatter_hash.values_at :model_class, :object
50
+ formatters[model_class] = object
51
+ end
52
+ formatters
53
+ end
54
+
55
+ end
@@ -7,10 +7,16 @@ class Moon::Application::Rack
7
7
  attr_reader :transponder
8
8
  attr_reader :environment
9
9
 
10
- def initialize
10
+ def initialize(application)
11
+ @application = application
11
12
  initialize_transponder
12
13
  end
13
14
 
15
+ def session=(session)
16
+ @session = session
17
+ set_session_secret
18
+ end
19
+
14
20
  def routes=(routes)
15
21
  @routes = routes
16
22
  map_routes
@@ -36,20 +42,23 @@ class Moon::Application::Rack
36
42
 
37
43
  def map_routes
38
44
  @routes.each do |route|
39
- Route.new(@transponder, route).define
45
+ Route.new(@application, @transponder, route).define
40
46
  end
41
47
  end
42
48
 
49
+ def set_session_secret
50
+ @transponder.set :session_secret, @session[:secret]
51
+ end
52
+
43
53
  # Abstraction for a application route
44
54
  class Route
45
55
 
46
56
  attr_reader :response
47
57
  attr_reader :rack_response
48
58
 
49
- def initialize(transponder, route)
50
- @transponder = transponder
59
+ def initialize(application, transponder, route)
60
+ @application, @transponder, @route = application, transponder, route
51
61
  @environment = @transponder.environment
52
- @route = route
53
62
  end
54
63
 
55
64
  def http_method
@@ -61,7 +70,8 @@ class Moon::Application::Rack
61
70
  end
62
71
 
63
72
  def actions
64
- @route[:actions] || [ ]
73
+ actions = @route[:actions]
74
+ actions.is_a?(Array) ? actions : [ actions ].compact
65
75
  end
66
76
 
67
77
  def define
@@ -82,7 +92,7 @@ class Moon::Application::Rack
82
92
 
83
93
  def build_context(session, params)
84
94
  @context = Moon::Context.new session, params
85
- @context.storage_name = Moon::Application.storage_name @environment
95
+ @context.application = @application
86
96
  end
87
97
 
88
98
  def perform_actions
@@ -2,7 +2,7 @@
2
2
  # Context class that contains anything necessary to run a guard, action or response builder.
3
3
  class Moon::Context
4
4
 
5
- attr_accessor :storage_name
5
+ attr_accessor :application
6
6
 
7
7
  def initialize(session = { }, parameters = { })
8
8
  @session, @parameters = session, parameters
@@ -2,21 +2,12 @@
2
2
  # Model formatter
3
3
  class Moon::Formatter
4
4
 
5
- autoload :Base, File.join(File.dirname(__FILE__), "formatter", "base")
6
5
  autoload :Generic, File.join(File.dirname(__FILE__), "formatter", "generic")
7
6
 
8
- def self.configuration=(value)
9
- @configuration = value
10
- end
11
-
12
- def self.configuration
13
- @configuration
14
- end
15
-
16
- def self.hash_for(model)
17
- formatter_class = (@configuration || { })[model.class]
18
- formatter_class ||= Moon::Formatter::Generic
19
- formatter_class.new(model).hash
7
+ def self.hash_for(formatters, model)
8
+ formatter = formatters[model.class]
9
+ formatter ||= Moon::Formatter::Generic.new
10
+ formatter.hash model
20
11
  end
21
12
 
22
13
  end
@@ -1,8 +1,9 @@
1
1
 
2
2
  # Outputs a hash with all instance variables of the given model.
3
- class Moon::Formatter::Generic < Moon::Formatter::Base
3
+ class Moon::Formatter::Generic
4
4
 
5
- def hash
5
+ def hash(model)
6
+ @model = model
6
7
  initialize_hash
7
8
  inject_instance_variables
8
9
  inject_id
@@ -18,12 +19,24 @@ class Moon::Formatter::Generic < Moon::Formatter::Base
18
19
  def inject_instance_variables
19
20
  @model.instance_variables.each do |variable_name|
20
21
  key = variable_name.to_s.sub(/^@/, "").to_sym
21
- @hash[key] = @model.instance_variable_get variable_name
22
+ value = @model.instance_variable_get variable_name
23
+ value.is_a?(GOM::Object::Proxy) ?
24
+ put_reference(key, value) :
25
+ put_regular(key, value)
22
26
  end
23
27
  end
24
28
 
29
+ def put_regular(key, value)
30
+ @hash[key] = value
31
+ end
32
+
33
+ def put_reference(key, reference)
34
+ @hash[:"#{key}_id"] = reference.id
35
+ end
36
+
25
37
  def inject_id
26
- @hash[:id] = GOM::Object.id @model
38
+ id = GOM::Object.id @model
39
+ @hash[:id] = id if id
27
40
  end
28
41
 
29
42
  end
@@ -3,6 +3,7 @@ require 'json'
3
3
  # Moon::Response::JSON is the base class for all json responses.
4
4
  class Moon::Response::JSON < Moon::Response::Base
5
5
 
6
+ autoload :Blank, File.join(File.dirname(__FILE__), "json", "blank")
6
7
  autoload :Collection, File.join(File.dirname(__FILE__), "json", "collection")
7
8
  autoload :Message, File.join(File.dirname(__FILE__), "json", "message")
8
9
  autoload :Model, File.join(File.dirname(__FILE__), "json", "model")
@@ -0,0 +1,9 @@
1
+
2
+ # Moon::Response::JSON::Blank provides a simple response with an empty hash
3
+ class Moon::Response::JSON::Blank < Moon::Response::JSON
4
+
5
+ def initialize
6
+ super 200, { }
7
+ end
8
+
9
+ end
@@ -2,9 +2,9 @@
2
2
  # Moon::Response::JSON::Collection provides a response that contains one single collection.
3
3
  class Moon::Response::JSON::Collection < Moon::Response::JSON
4
4
 
5
- def initialize(collection)
5
+ def initialize(collection, formatters)
6
6
  @collection = collection.map do |object|
7
- Moon::Formatter.hash_for object
7
+ Moon::Formatter.hash_for formatters, object
8
8
  end
9
9
  super 200, @collection
10
10
  end
@@ -2,15 +2,15 @@
2
2
  # Moon::Response::JSON::Collection provides a response that contains one single collection.
3
3
  class Moon::Response::JSON::Model < Moon::Response::JSON
4
4
 
5
- def initialize(key, object)
6
- @key, @object = key, object
5
+ def initialize(key, object, formatters)
6
+ @key, @object, @formatters = key, object, formatters
7
7
  super 200, hash
8
8
  end
9
9
 
10
10
  private
11
11
 
12
12
  def hash
13
- { @key.to_s => Moon::Formatter.hash_for(@object) }
13
+ { @key.to_s => Moon::Formatter.hash_for(@formatters, @object) }
14
14
  end
15
15
 
16
16
  end
@@ -6,66 +6,38 @@ class Moon::Validator
6
6
  autoload :Length, File.join(File.dirname(__FILE__), "validator", "length")
7
7
  autoload :Presence, File.join(File.dirname(__FILE__), "validator", "presence")
8
8
 
9
- attr_reader :model
9
+ attr_reader :model_class
10
10
 
11
- def initialize(model)
12
- @model = model
11
+ def initialize(context, checks)
12
+ @context, @checks = context, checks
13
13
  end
14
14
 
15
- def ok?
16
- perform_checks
17
- @ok
18
- end
19
-
20
- def messages
21
- perform_checks
15
+ def messages(model)
16
+ perform_checks model
22
17
  @messages
23
18
  end
24
19
 
25
20
  private
26
21
 
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
22
+ def perform_checks(model)
23
+ @messages = { }
24
+ @checks.each do |attribute, attribute_validator|
25
+ value = model.send attribute
26
+ perform_attribute_checks attribute, value, [ attribute_validator ].compact.flatten
31
27
  end
32
28
  end
33
29
 
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
30
+ def perform_attribute_checks(attribute, value, attribute_validators)
31
+ attribute_validators.each do |attribute_validator|
32
+ messages = attribute_validator.messages value, @context
33
+ add_messages attribute, messages
40
34
  end
41
35
  end
42
36
 
43
- def add_message(attribute, ok, message)
44
- return if ok || !message
37
+ def add_messages(attribute, messages)
38
+ return if messages.empty?
45
39
  @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
40
+ @messages[attribute] += messages
69
41
  end
70
42
 
71
43
  end
@@ -1,32 +1,13 @@
1
1
 
2
2
  # Value format validator.
3
3
  class Moon::Validator::Format
4
- extend Moon::Utility::Template
5
4
 
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
5
+ def initialize(format)
6
+ @format = format
18
7
  end
19
8
 
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
9
+ def messages(value, context)
10
+ value =~ @format ? [ ] : [ "Has a wrong format." ]
30
11
  end
31
12
 
32
13
  end
@@ -1,42 +1,17 @@
1
1
 
2
2
  # Value length validator.
3
3
  class Moon::Validator::Length
4
- extend Moon::Utility::Template
5
4
 
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
5
+ def initialize(range)
6
+ @range = range
23
7
  end
24
8
 
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
9
+ def messages(value, context)
10
+ raise ArgumentError, "Value doesn't response to :size" unless value.respond_to?(:size)
11
+ size, minimum, maximum = value.size, @range.min, @range.max
12
+ @range.include?(size) ?
13
+ [ ] :
14
+ [ "Must have at #{size < minimum ? ("least " + minimum.to_s) : ("most " + maximum.to_s)} entries." ]
40
15
  end
41
16
 
42
17
  end