hyper-store 1.0.alpha1.8 → 1.0.0.lap28
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -4
- data/.rubocop.yml +107 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/DOCS.md +312 -0
- data/Gemfile +1 -4
- data/LICENSE.txt +21 -0
- data/README.md +80 -0
- data/Rakefile +2 -0
- data/dciy.toml +3 -0
- data/hyper-store.gemspec +18 -20
- data/lib/hyper-store.rb +16 -19
- data/lib/hyper-store/class_methods.rb +32 -0
- data/lib/hyper-store/dispatch_receiver.rb +38 -0
- data/lib/hyper-store/instance_methods.rb +40 -0
- data/lib/hyper-store/mutator_wrapper.rb +71 -0
- data/lib/hyper-store/state_wrapper.rb +107 -0
- data/lib/hyper-store/state_wrapper/argument_validator.rb +91 -0
- data/lib/hyper-store/version.rb +3 -0
- data/lib/hyperloop/application/boot.rb +34 -0
- data/lib/hyperloop/store.rb +12 -0
- data/lib/hyperloop/store/mixin.rb +21 -0
- data/lib/react/observable.rb +29 -0
- data/lib/react/state.rb +143 -0
- metadata +64 -116
- data/lib/hyperstack/internal/store/class_methods.rb +0 -36
- data/lib/hyperstack/internal/store/dispatch_receiver.rb +0 -41
- data/lib/hyperstack/internal/store/instance_methods.rb +0 -45
- data/lib/hyperstack/internal/store/mutator_wrapper.rb +0 -84
- data/lib/hyperstack/internal/store/observable.rb +0 -33
- data/lib/hyperstack/internal/store/state_wrapper.rb +0 -116
- data/lib/hyperstack/internal/store/state_wrapper/argument_validator.rb +0 -93
- data/lib/hyperstack/legacy/store.rb +0 -23
- data/lib/hyperstack/legacy/store/version.rb +0 -7
@@ -1,36 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
module ClassMethods
|
5
|
-
attr_accessor :__shared_states, :__class_states, :__instance_states
|
6
|
-
|
7
|
-
def state(*args, &block)
|
8
|
-
# If we're passing in any arguments then we are calling the macro to define a state
|
9
|
-
if args.count > 0
|
10
|
-
singleton_class.__state_wrapper.class_state_wrapper
|
11
|
-
.define_state_methods(self, *args, &block)
|
12
|
-
# Otherwise we are just accessing it
|
13
|
-
else
|
14
|
-
@state ||= singleton_class.__state_wrapper.class_state_wrapper.new(self)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def mutate
|
19
|
-
@mutate ||= singleton_class.__state_wrapper.class_mutator_wrapper.new(self)
|
20
|
-
end
|
21
|
-
|
22
|
-
def __shared_states
|
23
|
-
@__shared_states ||= []
|
24
|
-
end
|
25
|
-
|
26
|
-
def __class_states
|
27
|
-
@__class_states ||= []
|
28
|
-
end
|
29
|
-
|
30
|
-
def __instance_states
|
31
|
-
@__instance_states ||= []
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
module DispatchReceiver
|
5
|
-
|
6
|
-
attr_accessor :params
|
7
|
-
|
8
|
-
def receives(*args, &block)
|
9
|
-
# Format the callback to be Proc or Nil
|
10
|
-
callback = format_callback(args)
|
11
|
-
|
12
|
-
if args.empty?
|
13
|
-
message = 'At least one operation must be passed in to the \'receives\' macro'
|
14
|
-
raise Legacy::Store::InvalidOperationError, message
|
15
|
-
end
|
16
|
-
|
17
|
-
# Loop through receivers and call callback and block on dispatch
|
18
|
-
args.each do |operation|
|
19
|
-
operation.on_dispatch do |params|
|
20
|
-
@params = params
|
21
|
-
|
22
|
-
callback.call if callback
|
23
|
-
yield params if block
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def format_callback(args)
|
31
|
-
if args.last.is_a?(Symbol)
|
32
|
-
method_name = args.pop
|
33
|
-
-> { send(:"#{method_name}") }
|
34
|
-
elsif args.last.is_a?(Proc)
|
35
|
-
args.pop
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
module InstanceMethods
|
5
|
-
def init_store
|
6
|
-
return if @__hyperstack_store_initialized
|
7
|
-
@__hyperstack_store_initialized = true
|
8
|
-
self.class.__instance_states.each do |instance_state|
|
9
|
-
# If the scope is shared then we initialize at the class level
|
10
|
-
next if instance_state[1][:scope] == :shared
|
11
|
-
|
12
|
-
# TODO: Figure out exactly how we're going to handle passing in procs and blocks together
|
13
|
-
# But for now...just do the proc first then the block
|
14
|
-
|
15
|
-
# First initialize value from initializer Proc
|
16
|
-
proc_value = initializer_value(instance_state[1][:initializer])
|
17
|
-
mutate.__send__(:"#{instance_state[0]}", proc_value)
|
18
|
-
|
19
|
-
# Then call the block if a block is passed
|
20
|
-
next unless instance_state[1][:block]
|
21
|
-
|
22
|
-
block_value = instance_eval(&instance_state[1][:block])
|
23
|
-
mutate.__send__(:"#{instance_state[0]}", block_value)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def state
|
28
|
-
@state ||= self.class.singleton_class.__state_wrapper.instance_state_wrapper.new(self)
|
29
|
-
end
|
30
|
-
|
31
|
-
def mutate
|
32
|
-
@mutate ||= self.class.singleton_class.__state_wrapper.instance_mutator_wrapper.new(self)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def initializer_value(initializer)
|
38
|
-
# We gotta check the arity because a Proc passed in directly from initializer has no args,
|
39
|
-
# but if we created one then we might have wanted self
|
40
|
-
initializer.arity > 0 ? initializer.call(self) : initializer.call
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
class MutatorWrapper < BaseStoreClass # < BasicObject
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def add_method(klass, method_name, opts = {})
|
8
|
-
define_method(:"#{method_name}") do |*args|
|
9
|
-
from = opts[:scope] == :shared ? klass.state.__from__ : __from__
|
10
|
-
from.init_store if from.respond_to? :init_store
|
11
|
-
current_value = State::Variable.get(from, method_name.to_s)
|
12
|
-
|
13
|
-
if args.count > 0
|
14
|
-
State::Variable.set(from, method_name.to_s, args[0])
|
15
|
-
current_value
|
16
|
-
else
|
17
|
-
State::Variable.set(from, method_name.to_s, current_value)
|
18
|
-
Observable.new(current_value) do |update|
|
19
|
-
State::Variable.set(from, method_name.to_s, update)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
initialize_values(klass, method_name, opts) if initialize_values?(opts)
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize_values?(opts)
|
28
|
-
[:class, :shared].include?(opts[:scope]) && (opts[:initializer] || opts[:block])
|
29
|
-
end
|
30
|
-
|
31
|
-
def initialize_values(klass, name, opts)
|
32
|
-
initializer = initializer_proc(opts[:initializer], klass, name) if opts[:initializer]
|
33
|
-
|
34
|
-
if initializer && opts[:block]
|
35
|
-
klass.receives(Hyperstack::Application::Boot, initializer) do
|
36
|
-
klass.mutate.__send__(:"#{name}", opts[:block].call)
|
37
|
-
end
|
38
|
-
elsif initializer
|
39
|
-
klass.receives(Hyperstack::Application::Boot, initializer)
|
40
|
-
elsif opts[:block]
|
41
|
-
klass.receives(Hyperstack::Application::Boot) do
|
42
|
-
klass.mutate.__send__(:"#{name}", opts[:block].call)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def initializer_proc(initializer, klass, name)
|
50
|
-
# We gotta check the arity because a Proc passed in directly from initializer has no args,
|
51
|
-
# but if we created one then we might have wanted the class
|
52
|
-
if initializer.arity > 0
|
53
|
-
-> { klass.mutate.__send__(:"#{name}", initializer.call(klass)) }
|
54
|
-
else
|
55
|
-
-> { klass.mutate.__send__(:"#{name}", initializer.call) }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
attr_accessor :__from__
|
61
|
-
|
62
|
-
def self.new(from)
|
63
|
-
instance = allocate
|
64
|
-
instance.__from__ = from
|
65
|
-
instance
|
66
|
-
end
|
67
|
-
|
68
|
-
def []=(name, value)
|
69
|
-
__send__(name, value)
|
70
|
-
end
|
71
|
-
|
72
|
-
def [](name)
|
73
|
-
__send__(name)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Any method_missing call will create a state and accessor with that name
|
77
|
-
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
|
78
|
-
(class << self; self end).add_method(nil, name)
|
79
|
-
__send__(name, *args, &block)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
class Observable
|
5
|
-
def initialize(value, on_change = nil, &block)
|
6
|
-
@value = value
|
7
|
-
@on_change = on_change || block
|
8
|
-
end
|
9
|
-
|
10
|
-
def method_missing(method_sym, *args, &block)
|
11
|
-
@value.send(method_sym, *args, &block).tap { |result| @on_change.call @value }
|
12
|
-
end
|
13
|
-
|
14
|
-
def respond_to?(method, *args)
|
15
|
-
if [:call, :to_proc].include? method
|
16
|
-
true
|
17
|
-
else
|
18
|
-
@value.respond_to? method, *args
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def call(new_value)
|
23
|
-
@on_change.call new_value
|
24
|
-
@value = new_value
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_proc
|
28
|
-
lambda { |arg = @value| @on_change.call arg }
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,116 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
class StateWrapper < BaseStoreClass
|
5
|
-
extend ArgumentValidator
|
6
|
-
|
7
|
-
class << self
|
8
|
-
attr_reader :instance_state_wrapper, :class_state_wrapper,
|
9
|
-
:instance_mutator_wrapper, :class_mutator_wrapper,
|
10
|
-
:wrappers
|
11
|
-
|
12
|
-
def inherited(subclass)
|
13
|
-
subclass.add_class_instance_vars(subclass) if self == StateWrapper
|
14
|
-
end
|
15
|
-
|
16
|
-
def add_class_instance_vars(subclass)
|
17
|
-
@shared_state_wrapper = subclass
|
18
|
-
@instance_state_wrapper = Class.new(@shared_state_wrapper)
|
19
|
-
@class_state_wrapper = Class.new(@shared_state_wrapper)
|
20
|
-
|
21
|
-
@shared_mutator_wrapper = Class.new(MutatorWrapper)
|
22
|
-
@instance_mutator_wrapper = Class.new(@shared_mutator_wrapper)
|
23
|
-
@class_mutator_wrapper = Class.new(@shared_mutator_wrapper)
|
24
|
-
|
25
|
-
@wrappers = [@instance_state_wrapper, @instance_mutator_wrapper,
|
26
|
-
@class_state_wrapper, @class_mutator_wrapper]
|
27
|
-
end
|
28
|
-
|
29
|
-
def define_state_methods(klass, *args, &block)
|
30
|
-
return self if args.empty?
|
31
|
-
|
32
|
-
name, opts = validate_args!(klass, *args, &block)
|
33
|
-
|
34
|
-
add_readers(klass, name, opts)
|
35
|
-
klass.singleton_class.state.add_error_methods(name, opts)
|
36
|
-
klass.singleton_class.state.add_methods(klass, name, opts)
|
37
|
-
klass.singleton_class.state.remove_methods(name, opts)
|
38
|
-
klass.send(:"__#{opts[:scope]}_states") << [name, opts]
|
39
|
-
end
|
40
|
-
|
41
|
-
def add_readers(klass, name, opts)
|
42
|
-
return unless opts[:reader]
|
43
|
-
|
44
|
-
if [:instance, :shared].include?(opts[:scope])
|
45
|
-
klass.class_eval do
|
46
|
-
define_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
if [:class, :shared].include?(opts[:scope])
|
51
|
-
klass.define_singleton_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def add_error_methods(name, opts)
|
56
|
-
return if opts[:scope] == :shared
|
57
|
-
|
58
|
-
[@shared_state_wrapper, @shared_mutator_wrapper].each do |klass|
|
59
|
-
klass.define_singleton_method(:"#{name}") do
|
60
|
-
'nope!'
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def add_methods(klass, name, opts)
|
66
|
-
instance_variable_get("@#{opts[:scope]}_state_wrapper").add_method(klass, name, opts)
|
67
|
-
instance_variable_get("@#{opts[:scope]}_mutator_wrapper").add_method(klass, name, opts)
|
68
|
-
end
|
69
|
-
|
70
|
-
def add_method(klass, method_name, opts = {})
|
71
|
-
define_method(:"#{method_name}") do
|
72
|
-
from = opts[:scope] == :shared ? klass.state.__from__ : @__from__
|
73
|
-
from.init_store if from.respond_to? :init_store
|
74
|
-
State::Variable.get(from, method_name.to_s)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def remove_methods(name, opts)
|
79
|
-
return unless opts[:scope] == :shared
|
80
|
-
|
81
|
-
wrappers.each do |wrapper|
|
82
|
-
wrapper.send(:remove_method, :"#{name}") if wrapper.respond_to?(:"#{name}")
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def default_scope(klass)
|
87
|
-
if self == klass.singleton_class.__state_wrapper.class_state_wrapper
|
88
|
-
:instance
|
89
|
-
else
|
90
|
-
:class
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
attr_accessor :__from__
|
96
|
-
|
97
|
-
def self.new(from)
|
98
|
-
instance = allocate
|
99
|
-
instance.__from__ = from
|
100
|
-
instance
|
101
|
-
end
|
102
|
-
|
103
|
-
def [](name)
|
104
|
-
__send__(name)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Any method_missing call will create a state and accessor with that name
|
108
|
-
def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
|
109
|
-
$method_missing = [name, *args]
|
110
|
-
(class << self; self end).add_method(nil, name) #(class << self; self end).superclass.add_method(nil, name)
|
111
|
-
__send__(name, *args, &block)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
module Hyperstack
|
2
|
-
module Internal
|
3
|
-
module Store
|
4
|
-
class StateWrapper < BaseStoreClass
|
5
|
-
module ArgumentValidator
|
6
|
-
def validate_args!(klass, *args, &block)
|
7
|
-
name, initial_value, opts = parse_arguments(*args, &block)
|
8
|
-
|
9
|
-
opts[:scope] ||= default_scope(klass)
|
10
|
-
opts[:initializer] = validate_initializer(initial_value, klass, opts)
|
11
|
-
opts[:block] = block if block
|
12
|
-
|
13
|
-
if opts[:reader]
|
14
|
-
opts[:reader] = opts[:reader] == true ? name : opts[:reader]
|
15
|
-
end
|
16
|
-
|
17
|
-
[name, opts]
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def invalid_option(message)
|
23
|
-
raise Legacy::Store::InvalidOptionError, message
|
24
|
-
end
|
25
|
-
|
26
|
-
# Parses the arguments given to get the name, initial_value (if any), and options
|
27
|
-
def parse_arguments(*args)
|
28
|
-
# If the only argument is a hash, the first key => value is name => inital_value
|
29
|
-
if args.first.is_a?(Hash)
|
30
|
-
# If the first key passed in is not the name, raise an error
|
31
|
-
if [:reader, :initializer, :scope].include?(args.first.keys.first.to_sym)
|
32
|
-
message = 'The name of the state must be specified first as '\
|
33
|
-
"either 'state :name' or 'state name: nil'"
|
34
|
-
invalid_option(message)
|
35
|
-
end
|
36
|
-
|
37
|
-
name, initial_value = args[0].shift
|
38
|
-
# Otherwise just the name is passed in by itself first
|
39
|
-
else
|
40
|
-
name = args.shift
|
41
|
-
end
|
42
|
-
|
43
|
-
# [name, initial_value (can be nil), args (if nil then return an empty hash)]
|
44
|
-
[name, initial_value, args[0] || {}]
|
45
|
-
end
|
46
|
-
|
47
|
-
# Converts the initialize option to a Proc
|
48
|
-
def validate_initializer(initial_value, klass, opts) # rubocop:disable Metrics/MethodLength
|
49
|
-
# If we pass in the name as a hash with a value ex: state foo: :bar,
|
50
|
-
# we just put that value inside a Proc and return that
|
51
|
-
if initial_value != nil
|
52
|
-
dup_or_return_intial_value(initial_value)
|
53
|
-
# If we pass in the initialize option
|
54
|
-
elsif opts[:initializer]
|
55
|
-
# If it's a Symbol we convert to to a Proc that calls the method on the instance
|
56
|
-
if [Symbol, String].include?(opts[:initializer].class)
|
57
|
-
method_name = opts[:initializer]
|
58
|
-
if [:class, :shared].include?(opts[:scope])
|
59
|
-
-> { klass.send(:"#{method_name}") }
|
60
|
-
else
|
61
|
-
->(instance) { instance.send(:"#{method_name}") }
|
62
|
-
end
|
63
|
-
# If it is already a Proc we do nothing and just return what was given
|
64
|
-
elsif opts[:initializer].is_a?(Proc)
|
65
|
-
opts[:initializer]
|
66
|
-
# If it's not a Proc or a String we raise an error and return an empty Proc
|
67
|
-
else
|
68
|
-
invalid_option("'state' option 'initialize' must either be a Symbol or a Proc")
|
69
|
-
-> {}
|
70
|
-
end
|
71
|
-
# Otherwise if it's not specified we just return an empty Proc
|
72
|
-
else
|
73
|
-
-> {}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# Dup the initial value if possible, otherwise just return it
|
78
|
-
# Ruby has no nice way of doing this...
|
79
|
-
def dup_or_return_intial_value(value)
|
80
|
-
value =
|
81
|
-
begin
|
82
|
-
value.dup
|
83
|
-
rescue
|
84
|
-
value
|
85
|
-
end
|
86
|
-
|
87
|
-
-> { value }
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|