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.
data/Gemfile CHANGED
@@ -1,6 +1,3 @@
1
1
  source 'https://rubygems.org'
2
- gem 'hyper-spec', path: '../hyper-spec'
3
- gem 'hyperstack-config', path: '../hyperstack-config'
4
- gem 'hyper-component', path: '../hyper-component'
5
- gem 'hyper-state', path: '../hyper-state'
2
+ gem "opal-jquery", git: "https://github.com/opal/opal-jquery.git", branch: "master"
6
3
  gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Adam Creekroad, Mitch VanDuyn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ <div class="githubhyperloopheader">
2
+
3
+ <p align="center">
4
+
5
+ <a href="http://ruby-hyperloop.io/" alt="Hyperloop" title="Hyperloop">
6
+ <img width="350px" src="http://ruby-hyperloop.io/images/hyperloop-github-logo.png">
7
+ </a>
8
+
9
+ </p>
10
+
11
+ <h2 align="center">The Complete Isomorphic Ruby Framework</h2>
12
+
13
+ <br>
14
+
15
+ <a href="http://ruby-hyperloop.io/" alt="Hyperloop" title="Hyperloop">
16
+ <img src="http://ruby-hyperloop.io/images/githubhyperloopbadge.png">
17
+ </a>
18
+
19
+ <a href="https://gitter.im/ruby-hyperloop/chat" alt="Gitter chat" title="Gitter chat">
20
+ <img src="http://ruby-hyperloop.io/images/githubgitterbadge.png">
21
+ </a>
22
+
23
+ [![Build Status](https://travis-ci.org/ruby-hyperloop/hyper-store.svg?branch=master)](https://travis-ci.org/ruby-hyperloop/hyper-store)
24
+ [![Codeship Status for ruby-hyperloop/hyper-store](https://app.codeship.com/projects/4454c560-d4ea-0134-7c96-362b4886dd22/status?branch=master)](https://app.codeship.com/projects/202301)
25
+ [![Gem Version](https://badge.fury.io/rb/hyper-store.svg)](https://badge.fury.io/rb/hyper-store)
26
+
27
+ <p align="center">
28
+ <img src="http://ruby-hyperloop.io/images/HyperStores.png" width="100" alt="Hyperstores">
29
+ </p>
30
+
31
+ </div>
32
+
33
+ ## Hyper-Store GEM is part of Hyperloop GEMS family
34
+
35
+ Build interactive Web applications quickly. Hyperloop encourages rapid development with clean, pragmatic design. With developer productivity as our highest goal, Hyperloop takes care of much of the hassle of Web development, so you can focus on innovation and delivering end-user value.
36
+
37
+ One language. One model. One set of tests. The same business logic and domain models running on the clients and the server. Hyperloop is fully integrated with Rails and also gives you unfettered access to the complete universe of JavaScript libraries (including React) from within your Ruby code. Hyperloop lets you build beautiful interactive user interfaces in Ruby.
38
+
39
+ Everything has a place in our architecture. Components deliver interactive user experiences, Operations encapsulate business logic, Models magically synchronize data between clients and servers, Policies govern authorization and Stores hold local state.
40
+
41
+ **Stores** are where the state of your Application lives. Anything but a completely static web page will have dynamic states that change because of user inputs, the passage of time, or other external events.
42
+
43
+ **Stores are Ruby classes that keep the dynamic parts of the state in special state variables**
44
+
45
+ ## Getting Started
46
+
47
+ 1. Update your Gemfile:
48
+
49
+ ```ruby
50
+ #Gemfile
51
+
52
+ gem 'hyperloop'
53
+ ```
54
+
55
+ 2. At the command prompt, update your bundle :
56
+
57
+ $ bundle update
58
+
59
+ 3. Run the hyperloop install generator:
60
+
61
+ $ rails g hyperloop:install
62
+
63
+ 4. Follow the guidelines to start developing your application. You may find
64
+ the following resources handy:
65
+ * [Getting Started with Hyperloop](http://ruby-hyperloop.io/start/components/)
66
+ * [Hyperloop Guides](http://ruby-hyperloop.io/docs/architecture)
67
+ * [Hyperloop Tutorial](http://ruby-hyperloop.io/tutorials)
68
+
69
+ ## Community
70
+
71
+ #### Getting Help
72
+ Please **do not post** usage questions to GitHub Issues. For these types of questions use our [Gitter chatroom](https://gitter.im/ruby-hyperloop/chat) or [StackOverflow](http://stackoverflow.com/questions/tagged/hyperloop).
73
+
74
+ #### Submitting Bugs and Enhancements
75
+ [GitHub Issues](https://github.com/ruby-hyperloop/hyperloop/issues) is for suggesting enhancements and reporting bugs. Before submiting a bug make sure you do the following:
76
+ * Check out our [contributing guide](https://github.com/ruby-hyperloop/hyperloop/blob/master/CONTRIBUTING.md) for info on our release cycle.
77
+
78
+ ## License
79
+
80
+ Hyperloop is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -5,6 +5,8 @@ RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  namespace :spec do
7
7
  task :prepare do
8
+ sh %{bundle update}
9
+ sh %{cd spec/test_app; bundle update}
8
10
  end
9
11
  end
10
12
 
data/dciy.toml ADDED
@@ -0,0 +1,3 @@
1
+ [dciy.commands]
2
+ prepare = ["rake spec:prepare"]
3
+ cibuild = ["bundle exec rake"]
data/hyper-store.gemspec CHANGED
@@ -1,43 +1,41 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'hyperstack/legacy/store/version'
4
+ require 'hyper-store/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'hyper-store'
8
- spec.version = Hyperstack::Legacy::Store::VERSION
8
+ spec.version = HyperStore::VERSION
9
9
  spec.authors = ['Mitch VanDuyn', 'Adam Creekroad', 'Jan Biedermann']
10
10
  spec.email = ['mitch@catprint.com', 'jan@kursator.com']
11
11
  spec.summary = 'Flux Stores and more for Hyperloop'
12
- spec.homepage = 'https://hyperstack.org'
12
+ spec.homepage = 'https://ruby-hyperloop.org'
13
13
  spec.license = 'MIT'
14
+ # spec.metadata = {
15
+ # "homepage_uri" => 'http://ruby-hyperloop.org',
16
+ # "source_code_uri" => 'https://github.com/ruby-hyperloop/hyper-component'
17
+ # }
18
+
14
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(gemfiles|spec)/}) }
15
20
  spec.bindir = 'exe'
16
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
17
22
  spec.require_paths = ['lib']
18
23
 
19
- spec.add_dependency 'hyper-state', Hyperstack::Legacy::Store::VERSION
20
- spec.add_dependency 'hyperstack-config', Hyperstack::Legacy::Store::VERSION
21
-
24
+ spec.add_dependency 'hyperloop-config', HyperStore::VERSION
25
+ spec.add_dependency 'opal', '>= 0.11.0', '< 0.12.0'
22
26
  spec.add_development_dependency 'bundler'
23
- spec.add_development_dependency 'chromedriver-helper'
24
- spec.add_development_dependency 'hyper-component', Hyperstack::Legacy::Store::VERSION
25
- spec.add_development_dependency 'hyper-spec', Hyperstack::Legacy::Store::VERSION
27
+ spec.add_development_dependency 'hyper-react', HyperStore::VERSION
28
+ spec.add_development_dependency 'hyper-spec', HyperStore::VERSION
26
29
  spec.add_development_dependency 'listen'
27
- # spec.add_development_dependency 'mini_racer', '~> 0.2.6'
30
+ spec.add_development_dependency 'mini_racer', '~> 0.1.15'
28
31
  spec.add_development_dependency 'opal-browser', '~> 0.2.0'
29
- spec.add_development_dependency 'opal-rails', '>= 0.9.4', '< 2.0'
30
- spec.add_development_dependency 'pry-rescue'
31
- spec.add_development_dependency 'pry-stack_explorer'
32
- spec.add_development_dependency 'puma'
33
- spec.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '>= 5.0.0', '< 7.0'
32
+ spec.add_development_dependency 'opal-rails', '~> 0.9.4'
33
+ spec.add_development_dependency 'pry-byebug'
34
+ spec.add_development_dependency 'rails', '>= 4.0.0'
34
35
  spec.add_development_dependency 'rake'
35
36
  spec.add_development_dependency 'react-rails', '>= 2.4.0', '< 2.5.0'
36
37
  spec.add_development_dependency 'rspec', '~> 3.7.0'
37
- spec.add_development_dependency 'rspec-rails'
38
38
  spec.add_development_dependency 'rspec-steps', '~> 2.1.1'
39
- spec.add_development_dependency 'rubocop' #, '~> 0.51.0'
40
- spec.add_development_dependency 'sqlite3', '~> 1.4.2'
41
- spec.add_development_dependency 'timecop', '~> 0.8.1'
42
-
39
+ spec.add_development_dependency 'rubocop', '~> 0.51.0'
40
+ spec.add_development_dependency 'sqlite3'
43
41
  end
data/lib/hyper-store.rb CHANGED
@@ -1,27 +1,24 @@
1
1
  require 'set'
2
- require 'hyperstack-config'
3
- require 'hyper-state'
4
- Hyperstack.import 'hyper-store'
2
+ require 'hyperloop-config'
3
+ Hyperloop.import 'hyper-store'
5
4
 
6
- module Hyperstack
7
- module Internal
8
- module Store
9
- # allows us to easily turn off BasicObject for debug
10
- class BaseStoreClass < BasicObject
11
- end
12
- end
5
+
6
+ module HyperStore # allows us to easily turn off BasicObject for debug
7
+ class BaseStoreClass < BasicObject
13
8
  end
14
9
  end
15
10
 
16
- require 'hyperstack/internal/store/class_methods'
17
- require 'hyperstack/internal/store/dispatch_receiver'
18
- require 'hyperstack/internal/store/instance_methods'
19
- require 'hyperstack/internal/store/mutator_wrapper'
20
- require 'hyperstack/internal/store/observable'
21
- require 'hyperstack/internal/store/state_wrapper/argument_validator'
22
- require 'hyperstack/internal/store/state_wrapper'
23
- require 'hyperstack/legacy/store'
24
- require 'hyperstack/legacy/store/version'
11
+ require 'hyper-store/class_methods'
12
+ require 'hyper-store/dispatch_receiver'
13
+ require 'hyper-store/instance_methods'
14
+ require 'hyper-store/mutator_wrapper'
15
+ require 'hyper-store/state_wrapper/argument_validator'
16
+ require 'hyper-store/state_wrapper'
17
+ require 'hyper-store/version'
18
+ require 'hyperloop/store'
19
+ require 'hyperloop/application/boot'
20
+ require 'hyperloop/store/mixin'
21
+ require 'react/state'
25
22
 
26
23
  if RUBY_ENGINE != 'opal'
27
24
  require 'opal'
@@ -0,0 +1,32 @@
1
+ module HyperStore
2
+ module ClassMethods
3
+ attr_accessor :__shared_states, :__class_states, :__instance_states
4
+
5
+ def state(*args, &block)
6
+ # If we're passing in any arguments then we are calling the macro to define a state
7
+ if args.count > 0
8
+ singleton_class.__state_wrapper.class_state_wrapper
9
+ .define_state_methods(self, *args, &block)
10
+ # Otherwise we are just accessing it
11
+ else
12
+ @state ||= singleton_class.__state_wrapper.class_state_wrapper.new(self)
13
+ end
14
+ end
15
+
16
+ def mutate
17
+ @mutate ||= singleton_class.__state_wrapper.class_mutator_wrapper.new(self)
18
+ end
19
+
20
+ def __shared_states
21
+ @__shared_states ||= []
22
+ end
23
+
24
+ def __class_states
25
+ @__class_states ||= []
26
+ end
27
+
28
+ def __instance_states
29
+ @__instance_states ||= []
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ module HyperStore
2
+ module DispatchReceiver
3
+ class InvalidOperationError < StandardError; end
4
+
5
+ attr_accessor :params
6
+
7
+ def receives(*args, &block)
8
+ # Format the callback to be Proc or Nil
9
+ callback = format_callback(args)
10
+
11
+ if args.empty?
12
+ message = 'At least one operation must be passed in to the \'receives\' macro'
13
+ raise InvalidOperationError, message
14
+ end
15
+
16
+ # Loop through receivers and call callback and block on dispatch
17
+ args.each do |operation|
18
+ operation.on_dispatch do |params|
19
+ @params = params
20
+
21
+ callback.call if callback
22
+ yield params if block
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def format_callback(args)
30
+ if args.last.is_a?(Symbol)
31
+ method_name = args.pop
32
+ -> { send(:"#{method_name}") }
33
+ elsif args.last.is_a?(Proc)
34
+ args.pop
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ module HyperStore
2
+ module InstanceMethods
3
+ def init_store
4
+ self.class.__instance_states.each do |instance_state|
5
+ # If the scope is shared then we initialize at the class level
6
+ next if instance_state[1][:scope] == :shared
7
+
8
+ # TODO: Figure out exactly how we're going to handle passing in procs and blocks together
9
+ # But for now...just do the proc first then the block
10
+
11
+ # First initialize value from initializer Proc
12
+ proc_value = initializer_value(instance_state[1][:initializer])
13
+ mutate.__send__(:"#{instance_state[0]}", proc_value)
14
+
15
+ # Then call the block if a block is passed
16
+ next unless instance_state[1][:block]
17
+
18
+ block_value = instance_eval(&instance_state[1][:block])
19
+ mutate.__send__(:"#{instance_state[0]}", block_value)
20
+ end
21
+
22
+ end
23
+
24
+ def state
25
+ @state ||= self.class.singleton_class.__state_wrapper.instance_state_wrapper.new(self)
26
+ end
27
+
28
+ def mutate
29
+ @mutate ||= self.class.singleton_class.__state_wrapper.instance_mutator_wrapper.new(self)
30
+ end
31
+
32
+ private
33
+
34
+ def initializer_value(initializer)
35
+ # We gotta check the arity because a Proc passed in directly from initializer has no args,
36
+ # but if we created one then we might have wanted self
37
+ initializer.arity > 0 ? initializer.call(self) : initializer.call
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,71 @@
1
+ module HyperStore
2
+ class MutatorWrapper < BaseStoreClass # < BasicObject
3
+
4
+ class << self
5
+ def add_method(klass, method_name, opts = {})
6
+ define_method(:"#{method_name}") do |*args|
7
+ from = opts[:scope] == :shared ? klass.state.__from__ : __from__
8
+ current_value = React::State.get_state(from, method_name.to_s)
9
+
10
+ if args.count > 0
11
+ React::State.set_state(from, method_name.to_s, args[0])
12
+ current_value
13
+ else
14
+ React::State.set_state(from, method_name.to_s, current_value)
15
+ React::Observable.new(current_value) do |update|
16
+ React::State.set_state(from, method_name.to_s, update)
17
+ end
18
+ end
19
+ end
20
+
21
+ initialize_values(klass, method_name, opts) if initialize_values?(opts)
22
+ end
23
+
24
+ def initialize_values?(opts)
25
+ [:class, :shared].include?(opts[:scope]) && (opts[:initializer] || opts[:block])
26
+ end
27
+
28
+ def initialize_values(klass, name, opts)
29
+ initializer = initializer_proc(opts[:initializer], klass, name) if opts[:initializer]
30
+
31
+ if initializer && opts[:block]
32
+ klass.receives(Hyperloop::Application::Boot, initializer) do
33
+ klass.mutate.__send__(:"#{name}", opts[:block].call)
34
+ end
35
+ elsif initializer
36
+ klass.receives(Hyperloop::Application::Boot, initializer)
37
+ elsif opts[:block]
38
+ klass.receives(Hyperloop::Application::Boot) do
39
+ klass.mutate.__send__(:"#{name}", opts[:block].call)
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def initializer_proc(initializer, klass, name)
47
+ # We gotta check the arity because a Proc passed in directly from initializer has no args,
48
+ # but if we created one then we might have wanted the class
49
+ if initializer.arity > 0
50
+ -> { klass.mutate.__send__(:"#{name}", initializer.call(klass)) }
51
+ else
52
+ -> { klass.mutate.__send__(:"#{name}", initializer.call) }
53
+ end
54
+ end
55
+ end
56
+
57
+ attr_accessor :__from__
58
+
59
+ def self.new(from)
60
+ instance = allocate
61
+ instance.__from__ = from
62
+ instance
63
+ end
64
+
65
+ # Any method_missing call will create a state and accessor with that name
66
+ def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
67
+ (class << self; self end).add_method(nil, name)
68
+ __send__(name, *args, &block)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,107 @@
1
+ module HyperStore
2
+ class StateWrapper < BaseStoreClass
3
+ extend ArgumentValidator
4
+
5
+ class << self
6
+ attr_reader :instance_state_wrapper, :class_state_wrapper,
7
+ :instance_mutator_wrapper, :class_mutator_wrapper,
8
+ :wrappers
9
+
10
+ def inherited(subclass)
11
+ subclass.add_class_instance_vars(subclass) if self == StateWrapper
12
+ end
13
+
14
+ def add_class_instance_vars(subclass)
15
+ @shared_state_wrapper = subclass
16
+ @instance_state_wrapper = Class.new(@shared_state_wrapper)
17
+ @class_state_wrapper = Class.new(@shared_state_wrapper)
18
+
19
+ @shared_mutator_wrapper = Class.new(MutatorWrapper)
20
+ @instance_mutator_wrapper = Class.new(@shared_mutator_wrapper)
21
+ @class_mutator_wrapper = Class.new(@shared_mutator_wrapper)
22
+
23
+ @wrappers = [@instance_state_wrapper, @instance_mutator_wrapper,
24
+ @class_state_wrapper, @class_mutator_wrapper]
25
+ end
26
+
27
+ def define_state_methods(klass, *args, &block)
28
+ return self if args.empty?
29
+
30
+ name, opts = validate_args!(klass, *args, &block)
31
+
32
+ add_readers(klass, name, opts)
33
+ klass.singleton_class.state.add_error_methods(name, opts)
34
+ klass.singleton_class.state.add_methods(klass, name, opts)
35
+ klass.singleton_class.state.remove_methods(name, opts)
36
+ klass.send(:"__#{opts[:scope]}_states") << [name, opts]
37
+ end
38
+
39
+ def add_readers(klass, name, opts)
40
+ return unless opts[:reader]
41
+
42
+ if [:instance, :shared].include?(opts[:scope])
43
+ klass.class_eval do
44
+ define_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
45
+ end
46
+ end
47
+
48
+ if [:class, :shared].include?(opts[:scope])
49
+ klass.define_singleton_method(:"#{opts[:reader]}") { state.__send__(:"#{name}") }
50
+ end
51
+ end
52
+
53
+ def add_error_methods(name, opts)
54
+ return if opts[:scope] == :shared
55
+
56
+ [@shared_state_wrapper, @shared_mutator_wrapper].each do |klass|
57
+ klass.define_singleton_method(:"#{name}") do
58
+ 'nope!'
59
+ end
60
+ end
61
+ end
62
+
63
+ def add_methods(klass, name, opts)
64
+ instance_variable_get("@#{opts[:scope]}_state_wrapper").add_method(klass, name, opts)
65
+ instance_variable_get("@#{opts[:scope]}_mutator_wrapper").add_method(klass, name, opts)
66
+ end
67
+
68
+ def add_method(klass, method_name, opts = {})
69
+ define_method(:"#{method_name}") do
70
+ from = opts[:scope] == :shared ? klass.state.__from__ : @__from__
71
+ React::State.get_state(from, method_name.to_s)
72
+ end
73
+ end
74
+
75
+ def remove_methods(name, opts)
76
+ return unless opts[:scope] == :shared
77
+
78
+ wrappers.each do |wrapper|
79
+ wrapper.send(:remove_method, :"#{name}") if wrapper.respond_to?(:"#{name}")
80
+ end
81
+ end
82
+
83
+ def default_scope(klass)
84
+ if self == klass.singleton_class.__state_wrapper.class_state_wrapper
85
+ :instance
86
+ else
87
+ :class
88
+ end
89
+ end
90
+ end
91
+
92
+ attr_accessor :__from__
93
+
94
+ def self.new(from)
95
+ instance = allocate
96
+ instance.__from__ = from
97
+ instance
98
+ end
99
+
100
+ # Any method_missing call will create a state and accessor with that name
101
+ def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
102
+ $method_missing = [name, *args]
103
+ (class << self; self end).add_method(nil, name) #(class << self; self end).superclass.add_method(nil, name)
104
+ __send__(name, *args, &block)
105
+ end
106
+ end
107
+ end