pavlov 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pavlov.rb ADDED
@@ -0,0 +1,41 @@
1
+ module Pavlov
2
+ # this method is also available as constantize in Rails,
3
+ # but we want to be able to write classes and/or tests without Rails
4
+ def self.get_class_by_string classname
5
+ classname.constantize
6
+ end
7
+
8
+ def self.string_to_classname string
9
+ string.to_s.camelize
10
+ end
11
+
12
+ def self.command command_name, *args
13
+ class_name = "Commands::"+string_to_classname(command_name)
14
+ klass = get_class_by_string(class_name)
15
+ klass.new(*args).call
16
+ end
17
+
18
+ def self.interactor command_name, *args
19
+ class_name = "Interactors::"+string_to_classname(command_name)
20
+ klass = get_class_by_string class_name
21
+ klass.new(*args).call
22
+ end
23
+
24
+ def self.query command_name, *args
25
+ class_name = "Queries::"+string_to_classname(command_name)
26
+ klass = get_class_by_string class_name
27
+ klass.new(*args).call
28
+ end
29
+ end
30
+
31
+ require_relative 'pavlov/helpers'
32
+ require_relative 'pavlov/utils'
33
+ require_relative 'pavlov/access_denied'
34
+ require_relative 'pavlov/validation_error'
35
+ require_relative 'pavlov/validations'
36
+ require_relative 'pavlov/operation'
37
+ require_relative 'pavlov/command'
38
+ require_relative 'pavlov/query'
39
+ require_relative 'pavlov/interactor'
40
+ require_relative 'pavlov/entity'
41
+ require_relative 'pavlov/version'
@@ -0,0 +1,4 @@
1
+ module Pavlov
2
+ class AccessDenied < StandardError
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support/concern'
2
+
3
+ module Pavlov
4
+ module Command
5
+ extend ActiveSupport::Concern
6
+ include Pavlov::Operation
7
+ def authorized?
8
+ true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,61 @@
1
+ require_relative 'helpers/safe_evaluator'
2
+
3
+ module Pavlov
4
+ class Entity
5
+ def self.attributes *args
6
+ args.each do |attribute_name|
7
+ define_attribute_writer attribute_name
8
+ attr_reader attribute_name
9
+ end
10
+ end
11
+
12
+ def self.new hash = {}, &block
13
+ super().send :mutate, hash, &block
14
+ end
15
+
16
+ def update hash = {}, &block
17
+ self.dup.send :mutate, hash, &block
18
+ end
19
+
20
+ private
21
+ def mutate hash, &block
22
+ copy_hash_values hash
23
+ safely_evaluate_against(&block) if block_given?
24
+ validate if respond_to? :validate
25
+ self
26
+ end
27
+
28
+ def copy_hash_values hash
29
+ make_temporary_mutatable do
30
+ hash.each {|key,value| send("#{key}=",value)}
31
+ end
32
+ end
33
+
34
+ def safely_evaluate_against &block
35
+ make_temporary_mutatable do
36
+ caller_instance = eval "self", block.binding
37
+ evaluator = Pavlov::Helpers::SafeEvaluator.new(self, caller_instance)
38
+ evaluator.instance_eval(&block)
39
+ end
40
+ self
41
+ end
42
+
43
+ def make_temporary_mutatable &block
44
+ @mutable = true
45
+ block.call
46
+ @mutable = false
47
+ end
48
+
49
+ def self.define_attribute_writer attribute_name
50
+ define_method "#{attribute_name}=" do |new_value|
51
+ raise_not_mutable unless @mutable
52
+ instance_variable_symbol = "@#{attribute_name}".to_sym
53
+ instance_variable_set instance_variable_symbol, new_value
54
+ end
55
+ end
56
+
57
+ def raise_not_mutable
58
+ raise "This entity is immutable, please use 'instance = #{self.class.name}.new do; self.attribute = 'value'; end' or 'instance = instance.update do; self.attribute = 'value'; end'."
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ module Pavlov
2
+ module Helpers
3
+ def interactor name, *args
4
+ args = add_pavlov_options args
5
+ Pavlov.interactor name, *args
6
+ end
7
+
8
+ def query name, *args
9
+ args = add_pavlov_options args
10
+ Pavlov.query name, *args, pavlov_options
11
+ end
12
+
13
+ def command name, *args
14
+ args = add_pavlov_options args
15
+ Pavlov.command name, *args, pavlov_options
16
+ end
17
+
18
+ def pavlov_options
19
+ {}
20
+ end
21
+
22
+ private
23
+ def add_pavlov_options args
24
+ # TODO: we should do this at a point where we know how many arguments we need
25
+ # so we can decide if we need to merge with another options object or
26
+ # just add it.
27
+ if pavlov_options != {}
28
+ args << pavlov_options
29
+ end
30
+ args
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ module Pavlov
2
+ module Helpers
3
+ class SafeEvaluator < BasicObject
4
+ def initialize target_instance, caller_instance
5
+ @target_instance, @caller_instance = target_instance, caller_instance
6
+ end
7
+
8
+ def method_missing method_name, *args
9
+ if method_name[-1] == '='
10
+ @target_instance.public_send method_name, *args
11
+ else
12
+ @caller_instance.send method_name, *args
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/inflector'
3
+
4
+ module Pavlov
5
+ module Interactor
6
+ extend ActiveSupport::Concern
7
+ include Pavlov::Operation
8
+
9
+ module ClassMethods
10
+ # make our interactors behave as Resque jobs
11
+ def perform(*args)
12
+ new(*args).call
13
+ end
14
+
15
+ def queue
16
+ @queue ||= :interactor_operations
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,72 @@
1
+ require 'active_support/concern'
2
+
3
+ module Pavlov
4
+ module Operation
5
+ extend ActiveSupport::Concern
6
+ include Pavlov::Helpers
7
+ include Pavlov::Validations
8
+ include Pavlov::Utils
9
+ def pavlov_options
10
+ @options
11
+ end
12
+
13
+ def raise_unauthorized(message='Unauthorized')
14
+ raise Pavlov::AccessDenied, message
15
+ end
16
+
17
+ def check_authority
18
+ raise_unauthorized unless respond_to? :authorized? and authorized?
19
+ end
20
+
21
+ def initialize *params
22
+ keys = (respond_to? :arguments) ? arguments : []
23
+ names = params.first(keys.length)
24
+ if params.length == keys.length + 1
25
+ @options = params.last
26
+ elsif params.length == keys.length
27
+ @options = {}
28
+ else
29
+ raise "wrong number of arguments."
30
+ end
31
+
32
+ (keys.zip names).each do |pair|
33
+ name = "@" + pair[0].to_s
34
+ value = pair[1]
35
+ instance_variable_set(name, value)
36
+ end
37
+
38
+ validate if respond_to? :validate
39
+ check_authority
40
+ finish_initialize if respond_to? :finish_initialize
41
+ end
42
+
43
+ def call
44
+ self.execute
45
+ end
46
+
47
+ module ClassMethods
48
+ # arguments :foo, :bar
49
+ #
50
+ # results in
51
+ #
52
+ # def initialize(foo, bar)
53
+ # @foo = foo
54
+ # @bar = bar
55
+ # end
56
+ def arguments *keys
57
+ define_method :arguments do
58
+ keys
59
+ end
60
+ end
61
+
62
+ # make our interactors behave as Resque jobs
63
+ def perform(*args)
64
+ new(*args).call
65
+ end
66
+
67
+ def queue
68
+ @queue || :interactor_operations
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_support/concern'
2
+
3
+ module Pavlov
4
+ module Query
5
+ extend ActiveSupport::Concern
6
+ include Pavlov::Operation
7
+
8
+ def authorized?
9
+ true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Pavlov
2
+ module Utils
3
+ def hash_with_index(index, list)
4
+ list.each_with_object({}) {|u, hash| hash[u.send(index)] = u}
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Pavlov
2
+ class ValidationError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'validations/errors'
2
+ require_relative 'validations/not_valid'
3
+
4
+ module Pavlov
5
+ module Validations
6
+ def errors
7
+ @errors ||= Pavlov::Validations::Errors.new
8
+ end
9
+
10
+ def validate_hexadecimal_string param_name, param
11
+ raise Pavlov::ValidationError, "#{param_name.to_s} should be an hexadecimal string." unless param.is_a? String and /\A[\da-fA-F]+\Z/.match param
12
+ end
13
+
14
+ def validate_regex param_name, param, regex, message
15
+ raise Pavlov::ValidationError, "#{param_name.to_s} #{message}" unless regex.match param
16
+ end
17
+
18
+ def validate_integer param_name, param
19
+ raise Pavlov::ValidationError, "#{param_name.to_s} should be an integer." unless param.is_a? Integer
20
+ end
21
+
22
+ def validate_in_set param_name, param, set
23
+ raise Pavlov::ValidationError, "#{param_name.to_s} should be on of these values: #{set.inspect}." unless set.include? param
24
+ end
25
+
26
+ def validate_string param_name, param
27
+ raise Pavlov::ValidationError, "#{param_name.to_s} should be a string." unless param.is_a? String
28
+ end
29
+
30
+ def validate_integer_string param_name, param
31
+ raise Pavlov::ValidationError, "#{param_name.to_s} should be an integer string." unless param.is_a? String and /\A\d+\Z/.match param
32
+ end
33
+
34
+ def validate_not_nil param_name, param
35
+ raise Pavlov::ValidationError, "#{param_name.to_s} should not be nil." if param.nil?
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ module Pavlov
2
+ module Validations
3
+ module Errors
4
+ include Enumerable
5
+
6
+ def initialize
7
+ @messages = {}
8
+ end
9
+
10
+ def each &block
11
+ @messages.each_key do |attribute|
12
+ @messages[attribute.to_s].each { |error| block.call attribute, error }
13
+ end
14
+ end
15
+
16
+ def add attribute, error_message
17
+ (@messages[attribute.to_s] ||= []) << error_message
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Pavlov
2
+ module Validations
3
+ class NotValid < StandardError
4
+ attr_accessor :errors
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Pavlov
2
+ # We're doing this because we might write tests that deal
3
+ # with other versions of bundler and we are unsure how to
4
+ # handle this better.
5
+ VERSION = "0.0.1"
6
+ end
metadata ADDED
@@ -0,0 +1,214 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pavlov
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark IJbema
9
+ - Tom de Vries
10
+ - Jan Paul Posma
11
+ - Remon Oldenbeuving
12
+ - Jan Deelstra
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2013-01-14 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: activesupport
20
+ requirement: !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: 3.2.7
26
+ type: :runtime
27
+ prerelease: false
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.7
34
+ - !ruby/object:Gem::Dependency
35
+ name: minitest
36
+ requirement: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: minitest-stub-const
52
+ requirement: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: guard
68
+ requirement: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: guard-bundler
84
+ requirement: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: guard-minitest
100
+ requirement: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: rb-fsevent
116
+ requirement: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ - !ruby/object:Gem::Dependency
131
+ name: rake
132
+ requirement: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ - !ruby/object:Gem::Dependency
147
+ name: benchmark-ips
148
+ requirement: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ type: :development
155
+ prerelease: false
156
+ version_requirements: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ description: Pavlov is a opinionated toolbox to help you architect your Ruby project.
163
+ email:
164
+ - jan+pavlov@deelstra.org
165
+ executables: []
166
+ extensions: []
167
+ extra_rdoc_files: []
168
+ files:
169
+ - lib/pavlov.rb
170
+ - lib/pavlov/access_denied.rb
171
+ - lib/pavlov/command.rb
172
+ - lib/pavlov/entity.rb
173
+ - lib/pavlov/helpers.rb
174
+ - lib/pavlov/helpers/safe_evaluator.rb
175
+ - lib/pavlov/interactor.rb
176
+ - lib/pavlov/operation.rb
177
+ - lib/pavlov/query.rb
178
+ - lib/pavlov/utils.rb
179
+ - lib/pavlov/validation_error.rb
180
+ - lib/pavlov/validations.rb
181
+ - lib/pavlov/validations/errors.rb
182
+ - lib/pavlov/validations/not_valid.rb
183
+ - lib/pavlov/version.rb
184
+ homepage: https://github.com/Factlink/pavlov/
185
+ licenses: []
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ! '>='
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ segments:
197
+ - 0
198
+ hash: -4075067472850494356
199
+ required_rubygems_version: !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ! '>='
203
+ - !ruby/object:Gem::Version
204
+ version: '0'
205
+ segments:
206
+ - 0
207
+ hash: -4075067472850494356
208
+ requirements: []
209
+ rubyforge_project:
210
+ rubygems_version: 1.8.23
211
+ signing_key:
212
+ specification_version: 3
213
+ summary: Infrastructure for defining your Ruby architecture.
214
+ test_files: []