droid_services 0.0.1

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/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Droidlabs
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # DroidServices
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'droid_services'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install droid_services
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ class DroidServices::Base
2
+ include DroidServices::Extensions::HasLogger
3
+ include DroidServices::Extensions::HasResponse
4
+
5
+ attr_accessor :params, :accessor
6
+
7
+ def initialize(options = {})
8
+ @accessor = options[:as]
9
+ @params = options[:params]
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ class DroidServices::BaseResource < DroidServices::Base
2
+ include DroidServices::Extensions::HasResource
3
+ include DroidServices::Extensions::HasCallbacks
4
+ include DroidServices::Extensions::HasService
5
+ include DroidServices::Extensions::HasDecorator
6
+ end
@@ -0,0 +1,17 @@
1
+ class DroidServices::Callbacks
2
+ attr_accessor :resource
3
+
4
+ def initialize(resource)
5
+ @resource = resource
6
+ end
7
+
8
+ def before_save; end
9
+ def before_create; end
10
+ def before_update; end
11
+ def before_destroy; end
12
+
13
+ def after_save; end
14
+ def after_create; end
15
+ def after_update; end
16
+ def after_destroy; end
17
+ end
@@ -0,0 +1,13 @@
1
+ class DroidServices::Decorator
2
+ attr_accessor :resource
3
+
4
+ def initialize(resource)
5
+ @resource = resource
6
+ end
7
+
8
+ def method_missing(method, *args, &block)
9
+ return super unless resource.respond_to?(method)
10
+
11
+ resource.send(method, *args, &block)
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_support/core_ext/object'
2
+ require 'active_support/core_ext/class'
3
+
4
+ module DroidServices::Extensions
5
+ module HasCallbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :resource_callbacks_handler_class
10
+ end
11
+
12
+ def invoke_callback(name, resource)
13
+ if resource_callbacks_handler_class
14
+ resource_callbacks_handler_class.new(resource).send(name)
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ def callbacks_handler(name, options = {})
20
+ self.resource_callbacks_handler_class = name
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ module DroidServices::Extensions
2
+ module HasDecorator
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :decorator_class_name
7
+ end
8
+
9
+ module ClassMethods
10
+ def decorator(name)
11
+ self.decorator_class_name = name.to_s
12
+ end
13
+ end
14
+
15
+ def decorated_collection
16
+ if decorable?
17
+ build_decorated_collection
18
+ else
19
+ build_collection
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def decorable?
26
+ !self.class.decorator_class_name.nil?
27
+ end
28
+
29
+ def decorator_class
30
+ self.class.decorator_class_name.to_s.classify.constantize
31
+ end
32
+
33
+ def prepare_collection_for_decorate
34
+ collection = build_collection
35
+
36
+ if collection.kind_of?(Array)
37
+ return collection
38
+ elsif collection.respond_to?(:all)
39
+ return collection.all
40
+ else
41
+ return Array.wrap(collection)
42
+ end
43
+ end
44
+
45
+ def build_decorated_collection
46
+ prepare_collection_for_decorate.map{|i| decorator_class.new(i)}
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ require 'yell'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/kernel/reporting'
4
+ require 'active_support/inflector'
5
+ require 'json'
6
+
7
+ module DroidServices::Extensions
8
+ module HasLogger
9
+ extend ActiveSupport::Concern
10
+
11
+ silence_warnings do
12
+ Yell::Formatter::PatternRegexp = /([^%]*)(%\d*)?(#{Yell::Formatter::PatternTable.keys.join('|')})?(.*)/m
13
+ end
14
+
15
+ included do
16
+ class << self
17
+ def logger
18
+ @logger ||= Yell.new do |l|
19
+ l.adapter :datefile, log_file,
20
+ level: (:debug..:fatal),
21
+ format: Yell.format( "%d [%5L] #%M at %F:%n\n%m\n\n", "%H:%M:%S" ),
22
+ date_pattern: "%Y-week-%V",
23
+ symlink_original_filename: true,
24
+ keep: 4
25
+ end
26
+ end
27
+
28
+ def format_message(message)
29
+ case message
30
+ when String
31
+ message
32
+ when Hash, Array
33
+ JSON::pretty_generate message
34
+ else message.inspect
35
+ end
36
+ end
37
+
38
+ private
39
+ def log_file
40
+ path = File.join(rails_log_path, "#{self.name.underscore}.log")
41
+ FileUtils.mkdir_p File.dirname(path)
42
+ path.to_s
43
+ end
44
+
45
+ def rails_log_path
46
+ File.join(Rails.root, 'log', Rails.env)
47
+ end
48
+ end
49
+
50
+ def logger
51
+ self.class.logger
52
+ end
53
+
54
+ def format_message(message)
55
+ self.class.format_message(message)
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,68 @@
1
+ module DroidServices::Extensions
2
+ module HasResource
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :resource_name
7
+ class_attribute :resource_class_name
8
+ end
9
+
10
+ def resource_class
11
+ if self.class.resource_class_name
12
+ self.class.resource_class_name.constantize
13
+ else
14
+ resource_name.classify.constantize
15
+ end
16
+ end
17
+
18
+ def resource_scope
19
+ resource_class
20
+ end
21
+
22
+ def collection_name
23
+ resource_name.pluralize
24
+ end
25
+
26
+ def resource_attributes
27
+ filter_attributes(dirty_resource_attributes)
28
+ end
29
+
30
+ def build_resource(id, attributes = {})
31
+ if id.present?
32
+ resource = find_resource(id)
33
+ resource.attributes = attributes
34
+ resource
35
+ else
36
+ resource_scope.new(default_resource_attributes.merge(attributes))
37
+ end
38
+ end
39
+
40
+ def build_collection
41
+ resource_scope
42
+ end
43
+
44
+ def find_resource(id)
45
+ resource_scope.find(id)
46
+ end
47
+
48
+ module ClassMethods
49
+ def resource(name, options = {})
50
+ self.resource_name = name.to_s
51
+ self.resource_class_name = options[:class_name]
52
+ end
53
+ end
54
+
55
+ private
56
+ def dirty_resource_attributes
57
+ params[resource_name] || {}
58
+ end
59
+
60
+ def default_resource_attributes
61
+ {}
62
+ end
63
+
64
+ def filter_attributes(attributes)
65
+ attributes
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,42 @@
1
+ module DroidServices::Extensions
2
+ module HasResponse
3
+ extend ActiveSupport::Concern
4
+
5
+ def add_error(error)
6
+ @_errors ||= []
7
+ @_errors << error
8
+ end
9
+
10
+ def assign_resource(resource_name, resource)
11
+ @_assignments ||= {}
12
+ @_assignments[resource_name.to_sym] = resource
13
+ end
14
+
15
+ def add_error_unless(error, condition)
16
+ add_error(error) unless condition
17
+ end
18
+
19
+ def add_errors_from(model)
20
+ @_errors ||= []
21
+ if model && model.errors.messages.present?
22
+ model.errors.messages.each do |key, value|
23
+ add_error "#{key}: #{value}"
24
+ end
25
+ end
26
+ end
27
+
28
+ def set_message(message)
29
+ @_message = message
30
+ end
31
+
32
+ def respond_with(resource, custom_resource_name=nil)
33
+ name = custom_resource_name||resource_name
34
+ result = respond_to?(:decorator_class) ? decorator_class.new(resource) : resource
35
+ response = DroidServices::Response.new(name, result)
36
+ response.message = @_message
37
+ response.errors = @_errors
38
+ response.assignments = @_assignments
39
+ response
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ module DroidServices::Extensions
2
+ module HasService
3
+ extend ActiveSupport::Concern
4
+
5
+ def collection
6
+ respond_with decorated_collection, collection_name
7
+ end
8
+
9
+ def build
10
+ respond_with build_resource(nil)
11
+ end
12
+
13
+ def create(custom_attributes=nil)
14
+ resource = build_resource(nil, custom_attributes||resource_attributes)
15
+ invoke_callback(:before_create, resource)
16
+ invoke_callback(:before_save, resource)
17
+ if resource.save
18
+ invoke_callback(:after_create, resource)
19
+ invoke_callback(:after_save, resource)
20
+ set_message("#{resource_name.titleize} successfully created")
21
+ else
22
+ add_errors_from(resource)
23
+ end
24
+ respond_with(resource)
25
+ end
26
+
27
+ def read
28
+ respond_with find_resource(params[:id])
29
+ end
30
+ alias_method :edit, :read
31
+
32
+
33
+ def update(custom_attributes=nil)
34
+ resource = build_resource(params[:id], custom_attributes||resource_attributes)
35
+ invoke_callback(:before_update, resource)
36
+ invoke_callback(:before_save, resource)
37
+ if resource.save
38
+ invoke_callback(:after_update, resource)
39
+ invoke_callback(:after_save, resource)
40
+ set_message("#{resource_name.titleize} successfully updated")
41
+ else
42
+ add_errors_from(resource)
43
+ end
44
+ respond_with(resource)
45
+ end
46
+
47
+ def destroy
48
+ resource = find_resource(params[:id])
49
+ invoke_callback(:before_destroy, resource)
50
+ resource.destroy
51
+ invoke_callback(:after_destroy, resource)
52
+ respond_with(resource)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,46 @@
1
+ class DroidServices::Response
2
+ attr_accessor :message, :errors, :assignments, :result, :resource_name
3
+
4
+ def initialize(resource_name, result)
5
+ @resource_name = resource_name
6
+ @result = result
7
+ @message = nil
8
+ @errors = []
9
+ @assignments = []
10
+ end
11
+
12
+ def with_errors(errors)
13
+ @errors = errors
14
+ self
15
+ end
16
+
17
+ def with_message(message)
18
+ @message = message
19
+ self
20
+ end
21
+
22
+ def success?
23
+ @errors.nil? || @errors.empty?
24
+ end
25
+
26
+ def failure?
27
+ !success?
28
+ end
29
+
30
+ def notice
31
+ message if success?
32
+ end
33
+
34
+ def error
35
+ message || errors.first if failure?
36
+ end
37
+
38
+ def has_error?(error)
39
+ errors && errors.include?(error)
40
+ end
41
+
42
+ def method_missing(method, *args, &block)
43
+ super unless assignments.has_key?(method)
44
+ assignments[method]
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module DroidServices
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,15 @@
1
+ require "droid_services/version"
2
+ require "droid_services/extensions/has_logger.rb"
3
+ require "droid_services/extensions/has_resource.rb"
4
+ require "droid_services/extensions/has_response.rb"
5
+ require "droid_services/extensions/has_callbacks.rb"
6
+ require "droid_services/extensions/has_service.rb"
7
+ require "droid_services/extensions/has_decorator.rb"
8
+ require "droid_services/base.rb"
9
+ require "droid_services/base_resource.rb"
10
+ require "droid_services/response.rb"
11
+ require "droid_services/callbacks.rb"
12
+ require "droid_services/decorator.rb"
13
+
14
+ module DroidServices
15
+ end
@@ -0,0 +1,180 @@
1
+ require 'spec_helper'
2
+
3
+ class Foo; end
4
+ module DroidServices
5
+ describe BaseResource do
6
+ describe 'it should behave like resource' do
7
+ context 'class methods' do
8
+ subject { DroidServices::BaseResource }
9
+ it{ should respond_to(:new) }
10
+ end
11
+ context 'instance methods' do
12
+ subject { DroidServices::BaseResource.new }
13
+ it{ should respond_to(:accessor) }
14
+ it{ should respond_to(:params) }
15
+ it{ should respond_to(:resource_name) }
16
+ it{ should respond_to(:resource_class) }
17
+ it{ should respond_to(:resource_scope) }
18
+ it{ should respond_to(:resource_attributes) }
19
+ it{ should respond_to(:build_resource) }
20
+ it{ should respond_to(:build_collection) }
21
+ it{ should respond_to(:find_resource) }
22
+ it{ should respond_to(:assign_resource) }
23
+ it{ should respond_to(:respond_with) }
24
+ it{ should respond_to(:set_message) }
25
+ it{ should respond_to(:add_error) }
26
+ it{ should respond_to(:add_error_unless) }
27
+ it{ should respond_to(:add_errors_from) }
28
+ it{ should respond_to(:invoke_callback) }
29
+ end
30
+ end
31
+
32
+ subject { DroidServices::BaseResource.new }
33
+ let(:resource) { mock.as_null_object }
34
+ let(:collection) { mock }
35
+ before do
36
+ subject.stub!(:params).and_return({id:123})
37
+ subject.stub!(:find_resource).and_return(resource)
38
+ subject.stub!(:resource_name).and_return('foo')
39
+ end
40
+
41
+ describe '#collection' do
42
+ it 'responds with decorated collection' do
43
+ subject.should_receive(:build_collection).and_return(collection)
44
+ subject.should_receive(:respond_with).with(collection, 'foos')
45
+ subject.collection
46
+ end
47
+ end
48
+
49
+ describe '#build' do
50
+ it 'responds with resource' do
51
+ subject.should_receive(:build_resource).with(nil).and_return(resource)
52
+ subject.should_receive(:respond_with).with(resource)
53
+ subject.build
54
+ end
55
+ end
56
+
57
+ describe '#edit' do
58
+ it 'responds with resource' do
59
+ subject.should_receive(:find_resource)
60
+ subject.should_receive(:respond_with).with(resource)
61
+ subject.edit
62
+ end
63
+ end
64
+
65
+ describe '#read' do
66
+ it 'responds with resource' do
67
+ subject.should_receive(:find_resource)
68
+ subject.should_receive(:respond_with).with(resource)
69
+ subject.read
70
+ end
71
+ end
72
+
73
+ describe '#destroy' do
74
+ it 'responds with resource' do
75
+ subject.should_receive(:find_resource)
76
+ subject.should_receive(:respond_with).with(resource)
77
+ subject.destroy
78
+ end
79
+
80
+ it 'destroys resource' do
81
+ resource.should_receive(:destroy)
82
+ subject.destroy
83
+ end
84
+
85
+ it 'invokes callbacks' do
86
+ subject.should_receive(:invoke_callback).with(:before_destroy, resource)
87
+ subject.should_receive(:invoke_callback).with(:after_destroy, resource)
88
+ subject.destroy
89
+ end
90
+ end
91
+
92
+ describe '#create' do
93
+ let(:params) { {foo: 'bar'} }
94
+ before { subject.stub!(:build_resource).and_return(resource) }
95
+ it 'responds with resource' do
96
+ subject.should_receive(:build_resource).with(nil, params)
97
+ subject.should_receive(:respond_with).with(resource)
98
+ subject.create(params)
99
+ end
100
+
101
+ context 'when resource successfully saved' do
102
+ before { resource.stub!(:save).and_return(true) }
103
+
104
+ it 'invokes callbacks' do
105
+ subject.should_receive(:invoke_callback).with(:before_save, resource)
106
+ subject.should_receive(:invoke_callback).with(:before_create, resource)
107
+ subject.should_receive(:invoke_callback).with(:after_save, resource)
108
+ subject.should_receive(:invoke_callback).with(:after_create, resource)
109
+ subject.create(params)
110
+ end
111
+
112
+ it 'sets message' do
113
+ subject.should_receive(:set_message).with('Foo successfully created')
114
+ subject.create(params)
115
+ end
116
+ end
117
+
118
+ context 'when resource could not be saved' do
119
+ before { resource.stub!(:save).and_return(false) }
120
+
121
+ it 'invokes callbacks' do
122
+ subject.should_receive(:invoke_callback).with(:before_save, resource)
123
+ subject.should_receive(:invoke_callback).with(:before_create, resource)
124
+ subject.should_not_receive(:invoke_callback).with(:after_save, resource)
125
+ subject.should_not_receive(:invoke_callback).with(:after_create, resource)
126
+ subject.create(params)
127
+ end
128
+
129
+ it 'sets error messages' do
130
+ subject.should_receive(:add_errors_from).with(resource)
131
+ subject.create(params)
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#update' do
137
+ let(:params) { {foo: 'bar'} }
138
+ before { subject.stub!(:build_resource).and_return(resource) }
139
+ it 'responds with resource' do
140
+ subject.should_receive(:build_resource).with(123, params)
141
+ subject.should_receive(:respond_with).with(resource)
142
+ subject.update(params)
143
+ end
144
+
145
+ context 'when resource successfully saved' do
146
+ before { resource.stub!(:save).and_return(true) }
147
+
148
+ it 'invokes callbacks' do
149
+ subject.should_receive(:invoke_callback).with(:before_save, resource)
150
+ subject.should_receive(:invoke_callback).with(:before_update, resource)
151
+ subject.should_receive(:invoke_callback).with(:after_save, resource)
152
+ subject.should_receive(:invoke_callback).with(:after_update, resource)
153
+ subject.update(params)
154
+ end
155
+
156
+ it 'sets message' do
157
+ subject.should_receive(:set_message).with('Foo successfully updated')
158
+ subject.update(params)
159
+ end
160
+ end
161
+
162
+ context 'when resource could not be saved' do
163
+ before { resource.stub!(:save).and_return(false) }
164
+
165
+ it 'invokes callbacks' do
166
+ subject.should_receive(:invoke_callback).with(:before_save, resource)
167
+ subject.should_receive(:invoke_callback).with(:before_update, resource)
168
+ subject.should_not_receive(:invoke_callback).with(:after_save, resource)
169
+ subject.should_not_receive(:invoke_callback).with(:after_update, resource)
170
+ subject.update(params)
171
+ end
172
+
173
+ it 'sets error messages' do
174
+ subject.should_receive(:add_errors_from).with(resource)
175
+ subject.update(params)
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ module DroidServices
4
+ describe Base do
5
+ describe '#new' do
6
+ subject { DroidServices::Base.new({as: :john, params: {foo: 'bar'}})}
7
+ its(:accessor) { should eq(:john) }
8
+ its(:params) { should eq({foo: 'bar'}) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module DroidServices
4
+ describe Callbacks do
5
+ subject { DroidServices::Callbacks.new(mock) }
6
+
7
+ it { should respond_to(:before_create) }
8
+ it { should respond_to(:after_create) }
9
+
10
+ it { should respond_to(:before_save) }
11
+ it { should respond_to(:after_save) }
12
+
13
+ it { should respond_to(:before_update) }
14
+ it { should respond_to(:after_update) }
15
+
16
+ it { should respond_to(:before_destroy) }
17
+ it { should respond_to(:after_destroy) }
18
+ end
19
+ end