rbdux 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e2b52fe3fdf03fb4b57e8875edf152dfa7dba39
4
+ data.tar.gz: 2a4cdd6c7dd86694bac2dee6af8fddeb793bdbed
5
+ SHA512:
6
+ metadata.gz: 7bbe51b93c102910fac95498117eff8774c924448bcb3b7e7308b8cf5f204f605321eb4bccc6c0cf2d1835d0674872cd1e6c1b20f64e6e16a7f38919d53f9b7a
7
+ data.tar.gz: 0b81e094159550b1bad1fe2508471abf88c9fb7a350c8ba2230f6c52320cca4e351f8b1bc61a836f918a2f5338dab073ba3fe612e00abacc80fb3f2d52559b0d
@@ -0,0 +1,66 @@
1
+ module Rbdux
2
+ class Action
3
+ class << self
4
+ def define(type_name, &block)
5
+ klass_name = "#{prepare_action_name(type_name)}Action"
6
+
7
+ return Object.const_get(klass_name) if Object.const_defined? klass_name
8
+
9
+ Object.const_set(klass_name, build_action_type(block))
10
+ end
11
+
12
+ private
13
+
14
+ class BaseAction
15
+ class << self
16
+ attr_reader :dispatch_func
17
+
18
+ def empty
19
+ new(nil, nil)
20
+ end
21
+
22
+ def with_payload(payload)
23
+ new(payload, nil)
24
+ end
25
+
26
+ def with_error(error)
27
+ new(nil, error)
28
+ end
29
+ end
30
+
31
+ attr_reader :payload, :error
32
+
33
+ def error?
34
+ !error.nil?
35
+ end
36
+
37
+ private
38
+
39
+ def initialize(payload, error)
40
+ @payload = payload
41
+ @error = error
42
+ end
43
+ end
44
+
45
+ def prepare_action_name(name)
46
+ camelize_action_name(name.tr('-', '_')).gsub('Action', '')
47
+ end
48
+
49
+ def camelize_action_name(name)
50
+ name.split('_').collect { |n| capitalize(n) }.join
51
+ end
52
+
53
+ def capitalize(name)
54
+ name_array = name.split('')
55
+ name_array.first.upcase!
56
+ name_array.join
57
+ end
58
+
59
+ def build_action_type(func)
60
+ Class.new(BaseAction) do
61
+ @dispatch_func = func
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,13 @@
1
+ module Rbdux
2
+ module Middleware
3
+ module_function
4
+
5
+ def dispatch_interceptor(store, action)
6
+ func = action.class.dispatch_func
7
+
8
+ return nil unless func
9
+
10
+ func.call(store, action)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,166 @@
1
+ require 'securerandom'
2
+
3
+ module Rbdux
4
+ class Store
5
+ class << self
6
+ @instance = nil
7
+
8
+ def instance
9
+ @instance ||= Store.new
10
+ end
11
+
12
+ def reset
13
+ @instance = nil
14
+ end
15
+
16
+ def with_state(state)
17
+ @instance = Store.new(state)
18
+ end
19
+
20
+ def method_missing(method, *args, &block)
21
+ return unless instance.respond_to?(method)
22
+
23
+ @instance.send(method, *args, &block)
24
+ end
25
+ end
26
+
27
+ attr_reader :state
28
+
29
+ def reduce(action, state_key = nil, &block)
30
+ add_reducer(action, state_key, true, block)
31
+
32
+ self
33
+ end
34
+
35
+ def reduce_and_merge(action, state_key = nil, &block)
36
+ add_reducer(action, state_key, false, block)
37
+
38
+ self
39
+ end
40
+
41
+ def when_merging(&block)
42
+ validate_functional_inputs(block)
43
+
44
+ @merge_func = block
45
+
46
+ self
47
+ end
48
+
49
+ def before(&block)
50
+ @before_middleware << block if block
51
+
52
+ self
53
+ end
54
+
55
+ def after(&block)
56
+ @after_middleware << block if block
57
+
58
+ self
59
+ end
60
+
61
+ def dispatch(action)
62
+ previous_state = state
63
+
64
+ dispatched_action = apply_before_middleware(action)
65
+
66
+ reducers.fetch(action.class.name, []).each do |reducer|
67
+ apply_reducer!(reducer, dispatched_action)
68
+ end
69
+
70
+ apply_after_middleware(previous_state, dispatched_action)
71
+
72
+ observers.values.each(&:call)
73
+ end
74
+
75
+ def subscribe(&block)
76
+ validate_functional_inputs(block)
77
+
78
+ subscriber_id = SecureRandom.uuid
79
+
80
+ observers[subscriber_id] = block
81
+
82
+ subscriber_id
83
+ end
84
+
85
+ def unsubscribe(subscriber_id)
86
+ observers.delete(subscriber_id)
87
+ end
88
+
89
+ private
90
+
91
+ Reducer = Struct.new(:state_key, :auto_merge, :func)
92
+
93
+ attr_reader :observers, :reducers
94
+
95
+ def initialize(state = {})
96
+ @state = state
97
+ @observers = {}
98
+ @reducers = {}
99
+ @before_middleware = []
100
+ @after_middleware = []
101
+ @merge_func = nil
102
+ end
103
+
104
+ def add_reducer(action, state_key, auto_merge, func)
105
+ validate_functional_inputs(func)
106
+
107
+ key = action.name
108
+
109
+ reducers[key] = [] unless reducers.key?(key)
110
+ reducers[key] << Reducer.new(state_key, auto_merge, func)
111
+ end
112
+
113
+ def apply_before_middleware(action)
114
+ dispatched_action = action
115
+
116
+ @before_middleware.each do |m|
117
+ new_action = m.call(self, dispatched_action)
118
+ dispatched_action = new_action unless new_action.nil?
119
+ end
120
+
121
+ dispatched_action
122
+ end
123
+
124
+ def apply_after_middleware(previous_state, action)
125
+ @after_middleware.each do |m|
126
+ m.call(previous_state, state, action)
127
+ end
128
+ end
129
+
130
+ def apply_reducer!(reducer, action)
131
+ reducer_params = [slice_state(reducer.state_key), action]
132
+ reducer_params << state unless reducer.auto_merge
133
+ new_state = reducer.func.call(*reducer_params)
134
+
135
+ return if new_state.nil?
136
+
137
+ @state = if reducer.auto_merge
138
+ merge_state(new_state, reducer.state_key)
139
+ else
140
+ new_state
141
+ end
142
+ end
143
+
144
+ def slice_state(state_key)
145
+ return state unless state_key
146
+
147
+ state[state_key]
148
+ end
149
+
150
+ def merge_state(new_state, state_key)
151
+ if @merge_func.nil?
152
+ default_merge(state, new_state, state_key)
153
+ else
154
+ @merge_func.call(state, new_state, state_key)
155
+ end
156
+ end
157
+
158
+ def default_merge(old_state, new_state, state_key)
159
+ old_state.dup.merge(state_key ? { state_key => new_state } : new_state)
160
+ end
161
+
162
+ def validate_functional_inputs(block)
163
+ raise ArgumentError, 'You must define a block.' unless block
164
+ end
165
+ end
166
+ end
data/lib/rbdux.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'rbdux/action'
2
+ require 'rbdux/store'
3
+ require 'rbdux/middleware/dispatch_interceptor'
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbdux
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Tompkins
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: josh@joshtompkins.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/rbdux.rb
20
+ - lib/rbdux/action.rb
21
+ - lib/rbdux/middleware/dispatch_interceptor.rb
22
+ - lib/rbdux/store.rb
23
+ homepage: https://github.com/jtompkins/rbdux
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.5.1
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: A simple one-way dataflow library, inspired by Redux
47
+ test_files: []