smash_the_state 1.3.0 → 1.4.0

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
  SHA256:
3
- metadata.gz: ffbecb9f1fd850903e2f88e7576bf684f03e38d14e70c3e07e61c0db93621961
4
- data.tar.gz: f259e6b012ff0bfca16320de3403967518de35e9c9b8e5d4baf53c5cd03ce6d2
3
+ metadata.gz: c239b6b0b7a6c2d025a9640781bfe3feea37887be11971a36641c21efdc89c29
4
+ data.tar.gz: 20b3cb0250ba5d589d76c327a32dcecb327a5626b2c300d0ff8962f0075f8e4c
5
5
  SHA512:
6
- metadata.gz: 143ca4fdccc3fd34300f133dae21d70e55ceff9121523a456ad499839403901d56d086059913defe0fddc6ba612046489065d8c9160e13b189da0a6a1cfe0719
7
- data.tar.gz: 2c40f91ac88831ea57b0ab44b24e4b514eea3524b5445c3cfb246dc12f1d5047731ed7620145f7a4d57478bf292fc93d540bb38916bc5c23a7e513aad83385db
6
+ metadata.gz: a1fb815ecf8fa1d672a400d1dea88ccafc00ecc571ccdbbe78803f55a12d25e343684ed62a2556711be9650b65251b50b97bec8adf1dd6396da10c845f240530
7
+ data.tar.gz: 7370311616f9a628e29abcc6c47d0a6728bf28eb274ebc5206e931cc1655741faa8a8b8c9c47908b80afbea36281d05dc14b02bd7c245cc2bed8dfe96bb41786
data/README.md CHANGED
@@ -96,24 +96,23 @@ class CreateAnalyticsOperation < SmashTheState::Operation
96
96
  end
97
97
  ```
98
98
 
99
- ## Dynamic State Classes (built at runtime)
99
+ ## Dynamic Schemas (built at runtime)
100
100
 
101
- Maybe your operation needs a more flexible schema than a static state class can provide. If you need your state class to be evaluated at runtime, you can omit a schema block and the raw params will be passed in as the initial state. From there you can create whatever state class you desire from inside the first step.
101
+ Maybe your operation needs a more flexible schema than a static state class can provide. Maybe you need to base your schema on some other data model that isn't available at the time the class is evaluated. If you need your state class to be evaluated at runtime, you can specify `dynamic_schema` with a block. The raw params hash will be passed in as the initial state. From there you can create whatever state class you desire at runtime. Be careful with this because this can quickly get out of hand. If you find yourself using dynamic schemas frequently, you may actually want distinct operations with static schemas.
102
102
 
103
103
  ```ruby
104
- class MyOperation < SmashTheState::Operation
105
- step :custom_state_class do |params|
106
- c = Operation::State.build do
107
- # create whatever state class you need at runtime
108
- attribute :some_name, :some_type
104
+ class BillingStuff < SmashTheState::Operation
105
+ dynamic_schema do |params|
106
+ # let's say we want to base our schema on an external service.
107
+ # we can call the service and build our schema off of the keys it returns
108
+ # let's say it's something like {name: "...", id: "...", is_paid: "..."}
109
+ BillingService.get_billing_things.keys do |key|
110
+ attribute key, :string
109
111
  end
110
-
111
- c.new(params)
112
112
  end
113
113
 
114
- step :do_more_things do |state, params|
115
- # params will be the initial params
116
- # ... and so on
114
+ step :do_more_things do |state|
115
+ # ... we receive a state with name, id, and is_paid attributes ...
117
116
  end
118
117
  end
119
118
  ```
@@ -35,6 +35,15 @@ module SmashTheState
35
35
  @state_class = Operation::State.build(&block)
36
36
  end
37
37
 
38
+ def dynamic_schema(&block)
39
+ sequence.add_step :_dynamic_schema do |params|
40
+ Operation::State.build(params, &block).new(params)
41
+ end
42
+
43
+ # make sure that the dynamic schema step that we just added above is always first
44
+ sequence.steps.unshift sequence.steps.pop
45
+ end
46
+
38
47
  def step(step_name, options = {}, &block)
39
48
  sequence.add_step(step_name, options, &block)
40
49
  end
@@ -7,6 +7,13 @@ module SmashTheState
7
7
  def initialize(wet_sequence)
8
8
  @wet_sequence = wet_sequence
9
9
  @dry_sequence = Operation::Sequence.new
10
+
11
+ # in the case of a dynamic schema, front-load it as the first step
12
+ dynamic_schema_step = @wet_sequence.dynamic_schema_step
13
+
14
+ return if dynamic_schema_step.nil?
15
+
16
+ step(dynamic_schema_step.name, &dynamic_schema_step.implementation)
10
17
  end
11
18
 
12
19
  def step(step_name, &block)
@@ -115,12 +115,26 @@ module SmashTheState
115
115
  @steps << step
116
116
  end
117
117
 
118
+ def dynamic_schema?
119
+ dynamic_schema_step.nil? == false
120
+ end
121
+
122
+ def dynamic_schema_step
123
+ steps_for_name(:_dynamic_schema).first
124
+ end
125
+
118
126
  private
119
127
 
128
+ def make_original_state(state)
129
+ return dynamic_schema_step.implementation.call(state, state, run_options) if dynamic_schema?
130
+
131
+ state.dup
132
+ end
133
+
120
134
  def run_steps(steps_to_run, state)
121
135
  # retain a copy of the original state so that we can refer to it for posterity as
122
136
  # the operation state gets mutated over time
123
- original_state = state.dup
137
+ original_state = make_original_state(state)
124
138
  current_step = nil
125
139
 
126
140
  steps_to_run.reduce(state) do |memo, s|
@@ -17,9 +17,9 @@ module SmashTheState
17
17
  class << self
18
18
  attr_accessor :representer
19
19
 
20
- def build(&block)
20
+ def build(params = nil, &block)
21
21
  Class.new(self).tap do |k|
22
- k.class_eval(&block)
22
+ k.class_exec(params, &block)
23
23
  end
24
24
  end
25
25
 
@@ -1,3 +1,3 @@
1
1
  module SmashTheState
2
- VERSION = "1.3.0".freeze
2
+ VERSION = "1.4.0".freeze
3
3
  end
@@ -191,6 +191,35 @@ describe SmashTheState::Operation do
191
191
  end
192
192
  end
193
193
 
194
+ describe "self#dynamic_schema" do
195
+ let!(:klass) do
196
+ Class.new(SmashTheState::Operation).tap do |k|
197
+ k.class_eval do
198
+ dynamic_schema do |params|
199
+ attribute "#{params[:adjective]}_#{params[:animal]}", :string
200
+ end
201
+
202
+ step :step_one do |state|
203
+ state.fat_ocelot = "maybe"
204
+ state
205
+ end
206
+
207
+ step :step_two do |state, original_state|
208
+ [state, original_state]
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ it "produces an 'inline' schema that is evaluated each time the operation "\
215
+ "runs while passing the 'original' dynamic state onto the next step" do
216
+ state, original_state = klass.call(adjective: "fat", animal: "ocelot", fat_ocelot: "no")
217
+
218
+ expect(state.fat_ocelot).to eq("maybe")
219
+ expect(original_state.fat_ocelot).to eq("no")
220
+ end
221
+ end
222
+
194
223
  describe "self#dry_run_sequence" do
195
224
  context "with a custom dry run sequence block" do
196
225
  before do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smash_the_state
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Connor
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-15 00:00:00.000000000 Z
11
+ date: 2019-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_model_attributes