pay_dirt 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 80daedc2e23621dd1692c5092c9cce375baf07a3
4
- data.tar.gz: 6db425bb6a5785adb71396cfa42ef26fdc29ecf0
3
+ metadata.gz: deb62d56966b35151bb79ca90be9ef8782668bb7
4
+ data.tar.gz: daec7ce9dc30b8c260de4e0eabac949c92e24401
5
5
  SHA512:
6
- metadata.gz: 6d5956acf790c96f0086e66b0312cbfe4037e421ff3f902a23386f937fe265f458738c299c8d0fd183efd942e860a2cc305cbf25bbf0f8d749806f95c05b6277
7
- data.tar.gz: 6cb08a75659b00259a7b273d6c0107c9f30a669817710fb97c2814984788cacb9065e4c4b000fa70622039eea600d7d6aaf20334197254123b687d7e8bda1183
6
+ metadata.gz: 55677cf4e2a832677f672c8b9e332a2f40ca3df46c1c517bbc0cef4365f5c90b4726da53b4cd4f48a556f3e83596860d98c08f9db4ca377ffbef765c8292f43a
7
+ data.tar.gz: b422b2fbf53018bd23d471cfebb351645d845459fe1298d225ea4ee643ed6f184c1c7e71dfbfbc92b5e1cd4dae57bc8bf058a3ab0cf90c7400e80e848147803c
data/.travis.yml CHANGED
@@ -8,8 +8,7 @@ rvm:
8
8
  - 2.0.0
9
9
  - 1.9.3
10
10
  - 1.9.2
11
- - jruby-19mode
11
+ - jruby
12
12
  - ruby-head
13
- - jruby-head
14
13
 
15
14
  script: bundle exec rake test
data/README.md CHANGED
@@ -1,9 +1,33 @@
1
1
  ## pay_dirt [![Gem Version](https://badge.fury.io/rb/pay_dirt.png)](http://badge.fury.io/rb/pay_dirt) [![Build Status](https://travis-ci.org/rthbound/pay_dirt.png?branch=master)](https://travis-ci.org/rthbound/pay_dirt) [![Coverage Status](https://coveralls.io/repos/rthbound/pay_dirt/badge.png?branch=master)](https://coveralls.io/r/rthbound/pay_dirt?branch=master) [![Code Climate](https://codeclimate.com/github/rthbound/pay_dirt.png)](https://codeclimate.com/github/rthbound/pay_dirt)
2
2
 
3
- #### A Ruby gem based on the "use case" pattern set forth in [opencurriculum-flashcards](https://github.com/isotope11/opencurriculum-flashcards)
4
-
5
3
  Provides the basic building blocks of a pattern capable of reducing a towering codebase to modular rubble (or more Ruby gems)
6
4
 
5
+ ### What is PayDirt
6
+
7
+ PayDirt gets its name from an 18th century gold mining idiom. One was said to have "struck pay dirt" when his pick axe revealed a vein of ore.
8
+ I hit pay dirt when I discovered this pattern. It provides me the freedom to build quickly with the confidence of knowing that testing will be a breeze.
9
+
10
+ ### What is the use case?
11
+
12
+ Its use case is gem making. It's for getting rid of callbacks and for shipping business logic off to the more suitable (and more portable) location.
13
+ It's for packaging use cases up in a modular fashion, where each unit expects to be provided certain dependencies and can be called to provide an expected result.
14
+ It makes sure you're using dependency injection so you can painlessly mock all your dependencies.
15
+
16
+ The basic idea:
17
+
18
+ 1. Initialize an object by supplying ALL dependencies as a single options hash.
19
+ 2. The object should have ONE public method, `#call`, which will return an expected result object.
20
+
21
+ What pay_dirt does to help:
22
+
23
+ 1. It will set instance variables from the hash of dependencies, using top level key-value pairs.
24
+ 2. It will not initialize (it WILL error) without all required dependencies.
25
+ 3. It allows you to set default values for any dependencies (just merge the `options` argument into your defaults hash before calling `#load_options`)
26
+
27
+ PayDirt also provides a `PayDirt::Result` object for your service objects to return (it will respond to `#successful?` and `#data`, see some examples). This is entirely optional, as this object can return whatever you like.
28
+
29
+ ### Getting on to it
30
+
7
31
  There are two ways to employ the pattern:
8
32
 
9
33
  1. use a class that inherits from [PayDirt::Base](https://github.com/rthbound/pay_dirt/blob/master/test/unit/pay_dirt/base_test.rb#L6-L24)
@@ -44,16 +68,18 @@ create a service object
44
68
  example
45
69
  -------
46
70
  ```
47
- $ thor pay_dirt:service_object:new digit_check -d fingers toes -D fingers:10 toes:10
48
- create lib/service_objects/digit_check.rb
71
+ $ thor pay_dirt:service_object:new quick/digit_check -d fingers toes nose -D fingers:10 toes:10
72
+ create lib/quick/digit_check.rb
73
+ create test/unit/quick/digit_check_test.rb
74
+ append test/minitest_helper.rb
49
75
  ```
50
76
 
51
- Running the above generator will create the following file
77
+ Running the above generator will create the following object
52
78
 
53
79
  ```ruby
54
80
  require 'pay_dirt'
55
81
 
56
- module ServiceObjects
82
+ module Quick
57
83
  class DigitCheck < PayDirt::Base
58
84
  def initialize(options = {})
59
85
  options = {
@@ -61,23 +87,58 @@ module ServiceObjects
61
87
  toes: 10,
62
88
  }.merge(options)
63
89
 
64
- load_options(:fingers, :toes, options)
90
+ load_options(:fingers, :toes, :nose, options)
65
91
  end
66
92
 
67
- def execute!
68
- result(true)
93
+ def call
94
+ return result(true)
95
+ end
96
+ end
97
+ end
98
+ ```
99
+
100
+ and the following unit test
101
+ ```ruby
102
+ require 'minitest_helper'
103
+
104
+ describe Quick::DigitCheck do
105
+ before do
106
+ @subject = Quick::DigitCheck
107
+ @params = {
108
+ fingers: MiniTest::Mock.new,
109
+ toes: MiniTest::Mock.new,
110
+ nose: MiniTest::Mock.new,
111
+ }
112
+ end
113
+
114
+ describe "as a class" do
115
+ it "initializes properly" do
116
+ @subject.new(@params).must_respond_to :call
117
+ end
118
+
119
+ it "errors when initialized without required dependencies" do
120
+ -> { @subject.new(@params.reject { |k| k.to_s == 'nose' }) }.must_raise RuntimeError
121
+ end
122
+ end
123
+
124
+ describe "as an instance" do
125
+ it "executes successfully" do
126
+ result = @subject.new(@params).call
127
+ result.successful?.must_equal true
128
+ result.must_be_kind_of PayDirt::Result
69
129
  end
70
130
  end
71
131
  end
72
132
  ```
73
133
 
74
134
  ### Usage:
135
+ The class generated can be used in the following manner:
75
136
  ```ruby
76
- require "service_objects/digit_check" #=> true
77
- ServiceObjects::DigitCheck.new.execute!
137
+ require "quick/digit_check" #=> true
138
+ Quick::DigitCheck.new(nose: true).call
78
139
  #=> #<PayDirt::Result:0xa0be85c @data=nil, @success=true>
79
140
  ```
80
- As you can see, we can now call `ServiceObjects::DigitCheck.new(fingers: 10, toes: 10).execute!`
141
+ As you can see, we can now call `Quick::DigitCheck.new(nose: true).call`
81
142
  and expect a successful return object. Where you take it from there is up to you.
82
143
 
83
144
  more examples
@@ -86,3 +147,8 @@ more examples
86
147
  2. [protected_record](https://github.com/rthbound/protected_record)
87
148
  3. [konamio](https://github.com/rthbound/konamio)
88
149
  4. [eenie_meenie](https://github.com/rthbound/eenie_meenie)
150
+ 5. [foaas](https://github.com/rthbound/foaas)
151
+ 6. [konami-fo](https://github.com/rthbound/konami-fo)
152
+ 7. [rungs](https://github.com/rthbound/rungs)
153
+
154
+ #### PayDirt is a Ruby gem based on the "use case" pattern set forth in [opencurriculum-flashcards](https://github.com/isotope11/opencurriculum-flashcards)
@@ -1,3 +1,3 @@
1
1
  module PayDirt
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.8"
3
3
  end
data/pay_dirt.thor CHANGED
@@ -2,11 +2,14 @@ module PayDirt
2
2
  class ServiceObject < Thor
3
3
  include Thor::Actions
4
4
 
5
- desc "new FILE", "create a service object"
5
+ desc "new FILE", "create a fully tested object (optionally, requires dependencies)"
6
6
  method_option :dependencies,
7
7
  type: :array,
8
8
  aliases: "-d",
9
- desc: "specify required dependencies"
9
+ desc: "specify required dependencies"
10
+ method_option :test_framework,
11
+ type: :string,
12
+ desc: "choose a testing framework"
10
13
  method_option :defaults,
11
14
  type: :hash,
12
15
  aliases: "-D",
@@ -26,73 +29,180 @@ module PayDirt
26
29
  class_names = file.split("/").map { |str| str.split("_").map{ |s| (s[0].upcase + s[1..-1]) }.join("") }
27
30
  @dependencies = options[:dependencies] || []
28
31
 
29
- # Favor 2 spaces
30
- @append = Proc.new { |depth, string| (@rets ||= "") << (" " * depth) + string }
31
-
32
- create_file "lib/service_objects/#{file}.rb" do
33
- open_class(class_names)
34
- write_initialize_method
35
- write_execute_method
36
-
37
- close_class(class_names)
38
-
39
- @rets
40
- end
32
+ create_object(file, class_names)
33
+ create_tests(file, class_names)
41
34
  end
42
35
 
43
36
  private
44
- desc "close_class CLASS_NAMES", "hide", hide: true
45
37
  def close_class(class_names)
46
- @append.call(@class_depth, "end\n") # Closes innermost class definition
38
+ append(@class_depth, "end\n") # Closes innermost class definition
47
39
 
48
- class_names[0..-1].each_with_index { |m,i| @append.call(@class_depth - (i + 1), "end\n") }
40
+ @class_depth.times { |i| append(@class_depth - (i + 1), "end\n") }
49
41
  end
50
42
 
51
- desc "open_class CLASS_NAMES", "hide", hide: true
52
43
  def open_class(class_names)
53
44
  @class_name = class_names.last
54
- @class_depth = class_names.length
55
- @inner_depth = class_names.length + 1
45
+ @class_depth = class_names.length - 1
46
+ @inner_depth = class_names.length
56
47
 
57
- @append.call(0, "require 'pay_dirt'\n\nmodule ServiceObjects\n")
58
- class_names[0..-2].each_with_index { |mod,i| @append.call(i.next, "module #{mod}\n") }
48
+ append(0, "require 'pay_dirt'\n\n")
49
+ class_names[0..-2].each_with_index { |mod,i| append(i, "module #{mod}\n") }
59
50
 
60
51
  if options[:include]
61
- @append.call(@class_depth, "class #{@class_name}\n")
62
- @append.call(@class_depth.next, "include PayDirt::UseCase\n")
52
+ append(@class_depth, "class #{@class_name}\n")
53
+ append(@inner_depth, "include PayDirt::UseCase\n")
63
54
  elsif options[:inherit]
64
- @append.call(@class_depth, "class #{@class_name} < PayDirt::Base\n")
55
+ append(@class_depth, "class #{@class_name} < PayDirt::Base\n")
65
56
  end
66
57
  end
67
58
 
68
59
  def write_execute_method
69
- # The execute! method
70
- @append.call(@inner_depth, "def execute!\n")
71
- @append.call(@inner_depth.next, "return result(true)\n")
72
- @append.call(@inner_depth, "end\n")
60
+ # The call method
61
+ append(@inner_depth, "def call\n")
62
+ append(@inner_depth.next, "return result(true)\n")
63
+ append(@inner_depth, "end\n")
73
64
  end
74
65
 
75
66
  def write_initialize_method
76
- @append.call(@inner_depth, "def initialize(options = {})\n")
67
+ append(@inner_depth, "def initialize(options = {})\n")
77
68
 
78
69
  set_defaults if options[:defaults]
79
70
  call_load_options
80
71
 
81
- @append.call(@inner_depth, "end\n\n")
72
+ append(@inner_depth, "end\n\n")
82
73
  end
83
74
 
84
75
  def call_load_options
85
- @append.call(@inner_depth.next, "load_options(")
86
- @dependencies.each { |dep| @append.call(0, ":#{dep}, ") }
87
- @append.call(0, "options)\n")
76
+ append(@inner_depth.next, "# sets instance variables from key value pairs,\n")
77
+ append(@inner_depth.next, "# will fail if any keys given before options aren't in options\n")
78
+ append(@inner_depth.next, "load_options(")
79
+ @dependencies.each { |dep| append(0, ":#{dep}, ") }
80
+ append(0, "options)\n")
88
81
  end
89
82
 
90
83
  def set_defaults
91
- @append.call(@inner_depth.next, "options = {\n")
84
+ append(@inner_depth.next, "options = {\n")
85
+
86
+ options[:defaults].each { |k,v| append(@inner_depth + 2, "#{k}: #{v}" + ",\n") }
87
+
88
+ append(@inner_depth.next, "}.merge(options)\n\n")
89
+ end
90
+
91
+ # TESTS!
92
+ def open_test_class(class_names, file)
93
+ case options[:test_framework]
94
+ when "minitest", "mini_test"
95
+ append(0, "require 'minitest_helper'\n\n")
96
+ append_to_file "test/minitest_helper.rb" do
97
+ "require \"#{file}\"\n"
98
+ end
99
+ else
100
+ append(0, "require 'test_helper'\n\n")
101
+ append_to_file "test/test_helper.rb" do
102
+ "require \"#{file}\"\n"
103
+ end
104
+ end
105
+ append(0, "describe #{ class_string(class_names) } do\n")
106
+ end
107
+
108
+ def mock_test_dependencies
109
+ append(2, "@params = {\n")
110
+
111
+ @dependencies.each do |dep|
112
+ append(3, "#{dep}: MiniTest::Mock.new,\n")
113
+ end
114
+
115
+ append(2, "}\n")
116
+ end
117
+
118
+ def add_before_hook(class_names)
119
+ append(1, "before do\n")
120
+ append(2, "@subject = #{ class_string(class_names) }\n")
121
+ mock_test_dependencies
122
+ append(1, "end\n")
123
+ end
124
+
125
+ def assert_this(assertion, asserts)
126
+ append(2, "it \"#{assertion}\" do\n")
127
+
128
+ asserts.each do |s|
129
+ append(3, "#{s}\n")
130
+ end
131
+
132
+ append(2, "end\n")
133
+ end
92
134
 
93
- options[:defaults].each { |k,v| @append.call(@inner_depth + 2, "#{k}: #{v}" + ",\n") }
135
+ def assert_error_without_dependencies
136
+ assert_this("errors when initialized without required dependencies", @dependencies.reject { |d|
137
+ options[:defaults] && options[:defaults].keys.include?(d)
138
+ }.map { |required_dep|
139
+ "-> { @subject.new(@params.reject { |k| k.to_s == '#{ required_dep }' }) }.must_raise RuntimeError"
140
+ })
141
+ end
142
+
143
+ def assert_wont_error_with_all_dependencies
144
+ assert_this("initializes properly", ["@subject.new(@params).must_respond_to :call"])
145
+ end
146
+
147
+ def assert_returns_a_successful_result
148
+ assert_this("executes successfully", [
149
+ "result = @subject.new(@params).call",
150
+ "result.successful?.must_equal true",
151
+ "result.must_be_kind_of PayDirt::Result"
152
+ ])
153
+ end
154
+
155
+ def context_class
156
+ append(1, "describe \"as a class\" do\n")
157
+ assert_wont_error_with_all_dependencies
158
+ append(0, "\n")
159
+ assert_error_without_dependencies
160
+ append(1, "end\n")
161
+ end
162
+
163
+ def context_instance
164
+ append(1, "describe \"as an instance\" do\n")
165
+ assert_returns_a_successful_result
166
+ append(1, "end\n")
167
+ end
168
+
169
+ def close_test_class
170
+ append(0, "end")
171
+ end
172
+
173
+ def create_object(file, class_names)
174
+ @rets = nil
175
+ create_file "lib/#{file}.rb" do
176
+ open_class(class_names)
177
+ write_initialize_method
178
+ write_execute_method
179
+
180
+ close_class(class_names)
181
+
182
+ @rets
183
+ end
184
+ end
185
+
186
+ def create_tests(file, class_names)
187
+ @rets = nil
188
+ create_file "test/unit/#{file}_test.rb" do
189
+ open_test_class(class_names, file)
190
+ add_before_hook(class_names)
191
+ append(0, "\n")
192
+ context_class
193
+ append(0, "\n")
194
+ context_instance
195
+ close_test_class
196
+ @rets
197
+ end
198
+ end
199
+
200
+ def append(depth, string)
201
+ (@rets ||= "") << (" " * depth) + string
202
+ end
94
203
 
95
- @append.call(@inner_depth.next, "}.merge(options)\n\n")
204
+ def class_string(names)
205
+ names.map(&:to_s).join("::")
96
206
  end
97
207
  end
98
208
  end
@@ -13,7 +13,7 @@ describe PayDirt::Base do
13
13
  load_options(:the_question, :the_secret, options)
14
14
  end
15
15
 
16
- def execute!
16
+ def call
17
17
  @the_secret == 42 and return result(true, @the_secret )
18
18
  return result(false)
19
19
  end
@@ -33,26 +33,26 @@ describe PayDirt::Base do
33
33
  end
34
34
 
35
35
  it "won't error if defaults were supplied for an omitted option" do
36
- @use_case.new(the_secret: :not_telling).must_respond_to :execute!
36
+ @use_case.new(the_secret: :not_telling).must_respond_to :call
37
37
  end
38
38
 
39
- it "can execute successfully" do
39
+ it "can be called successfully" do
40
40
  dependencies = {
41
41
  the_secret: 42
42
42
  }
43
43
 
44
- result = @use_case.new(dependencies).execute!
44
+ result = @use_case.new(dependencies).call
45
45
 
46
46
  result.successful?.must_equal true
47
47
  result.must_be_kind_of PayDirt::Result
48
48
  end
49
49
 
50
- it "can execute unsuccessfully" do
50
+ it "can be called unsuccessfully" do
51
51
  dependencies = {
52
52
  the_secret: :i_dunno
53
53
  }
54
54
 
55
- result = @use_case.new(dependencies).execute!
55
+ result = @use_case.new(dependencies).call
56
56
 
57
57
  result.successful?.must_equal false
58
58
  end
@@ -19,7 +19,7 @@ describe PayDirt::Result do
19
19
  load_options(:success, :data, options)
20
20
  end
21
21
 
22
- def execute!
22
+ def call
23
23
  result(@success, @data)
24
24
  end
25
25
  end
@@ -28,15 +28,15 @@ describe PayDirt::Result do
28
28
  end
29
29
 
30
30
  it "can succeed" do
31
- @pay_dirt.new(success: true, data: "yum").execute!.successful?.must_equal true
31
+ @pay_dirt.new(success: true, data: "yum").call.successful?.must_equal true
32
32
  end
33
33
 
34
34
  it "can be unsuccessful" do
35
- @pay_dirt.new(success: false, data: "gross").execute!.successful?.must_equal false
35
+ @pay_dirt.new(success: false, data: "gross").call.successful?.must_equal false
36
36
  end
37
37
 
38
38
  it "provides access to data" do
39
- @pay_dirt.new(success: true, data: "yum").execute!.data.must_equal "yum"
39
+ @pay_dirt.new(success: true, data: "yum").call.data.must_equal "yum"
40
40
  end
41
41
  end
42
42
  end
@@ -16,7 +16,7 @@ describe PayDirt::UseCase do
16
16
  load_options(:the_secret_to_life_the_universe_and_everything, options)
17
17
  end
18
18
 
19
- def execute!
19
+ def call
20
20
  if @the_secret_to_life_the_universe_and_everything == 42
21
21
  return PayDirt::Result.new(data: return_value, success: true)
22
22
  else
@@ -46,30 +46,30 @@ describe PayDirt::UseCase do
46
46
  end
47
47
 
48
48
  it "must not error when options with defaults are omitted" do
49
- @use_case.new({}).must_respond_to :execute!
49
+ @use_case.new({}).must_respond_to :call
50
50
  end
51
51
 
52
52
  it "loads options that are not required" do
53
- result = @use_case.new({ some_random_option: true }).execute!
53
+ result = @use_case.new({ some_random_option: true }).call
54
54
  assert result.data[:some_random_option]
55
55
  end
56
56
 
57
- it "can execute successfully" do
57
+ it "can be called successfully" do
58
58
  dependencies = {
59
59
  }
60
60
 
61
- result = @use_case.new(dependencies).execute!
61
+ result = @use_case.new(dependencies).call
62
62
 
63
63
  result.successful?.must_equal true
64
64
  result.must_be_kind_of PayDirt::Result
65
65
  end
66
66
 
67
- it "can execute unsuccessfully" do
67
+ it "can be called unsuccessfully" do
68
68
  dependencies = {
69
69
  the_secret_to_life_the_universe_and_everything: :i_dunno
70
70
  }
71
71
 
72
- result = @use_case.new(dependencies).execute!
72
+ result = @use_case.new(dependencies).call
73
73
 
74
74
  result.successful?.must_equal false
75
75
  end
@@ -86,7 +86,7 @@ describe PayDirt::UseCase do
86
86
  load_options(:required_option_with_default_value, :required_option, options)
87
87
  end
88
88
 
89
- def execute!
89
+ def call
90
90
  if !@required_option_with_default_value
91
91
  return PayDirt::Result.new(data: return_value, success: true)
92
92
  else
@@ -105,15 +105,15 @@ describe PayDirt::UseCase do
105
105
  end
106
106
 
107
107
  # Cheating by not injecting all dependencies
108
- result = SomeThing.new(required_option: true).execute! # Returns a PayDirt::Result
108
+ result = SomeThing.new(required_option: true).call # Returns a PayDirt::Result
109
109
  assert !result.successful? #=> false
110
110
  result.data[:optional_option].must_be_nil #=> nil
111
111
  # Playing nice and injecting all required dependencies
112
- result = SomeThing.new(required_option: true, required_option_with_default_value: false).execute!
112
+ result = SomeThing.new(required_option: true, required_option_with_default_value: false).call
113
113
  assert result.successful? #=> true
114
114
  result.data[:optional_option].must_be_nil #=> nil
115
115
  # Making use of an optional option
116
- result = SomeThing.new(required_option: true, optional_option: true).execute!
116
+ result = SomeThing.new(required_option: true, optional_option: true).call
117
117
  assert !result.successful? #=> false
118
118
  assert result.data[:optional_option] #=> true
119
119
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pay_dirt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tad Hosford
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-30 00:00:00.000000000 Z
11
+ date: 2013-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest