pavlov 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/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: []