volt-repo_cache 0.1.4

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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +314 -0
  7. data/Rakefile +1 -0
  8. data/lib/volt/repo_cache.rb +6 -0
  9. data/lib/volt/repo_cache/association.rb +100 -0
  10. data/lib/volt/repo_cache/cache.rb +116 -0
  11. data/lib/volt/repo_cache/collection.rb +259 -0
  12. data/lib/volt/repo_cache/model.rb +671 -0
  13. data/lib/volt/repo_cache/model_array.rb +169 -0
  14. data/lib/volt/repo_cache/util.rb +78 -0
  15. data/lib/volt/repo_cache/version.rb +5 -0
  16. data/spec/dummy/.gitignore +9 -0
  17. data/spec/dummy/README.md +4 -0
  18. data/spec/dummy/app/main/assets/css/app.css.scss +1 -0
  19. data/spec/dummy/app/main/config/dependencies.rb +11 -0
  20. data/spec/dummy/app/main/config/initializers/boot.rb +10 -0
  21. data/spec/dummy/app/main/config/routes.rb +14 -0
  22. data/spec/dummy/app/main/controllers/main_controller.rb +27 -0
  23. data/spec/dummy/app/main/models/customer.rb +4 -0
  24. data/spec/dummy/app/main/models/order.rb +6 -0
  25. data/spec/dummy/app/main/models/product.rb +5 -0
  26. data/spec/dummy/app/main/models/user.rb +12 -0
  27. data/spec/dummy/app/main/views/main/about.html +7 -0
  28. data/spec/dummy/app/main/views/main/index.html +6 -0
  29. data/spec/dummy/app/main/views/main/main.html +29 -0
  30. data/spec/dummy/config.ru +4 -0
  31. data/spec/dummy/config/app.rb +147 -0
  32. data/spec/dummy/config/base/index.html +15 -0
  33. data/spec/dummy/config/initializers/boot.rb +4 -0
  34. data/spec/integration/sample_integration_spec.rb +11 -0
  35. data/spec/sample_spec.rb +7 -0
  36. data/spec/spec_helper.rb +18 -0
  37. data/volt-repo_cache.gemspec +38 -0
  38. metadata +287 -0
@@ -0,0 +1,169 @@
1
+ require 'volt/reactive/reactive_array'
2
+
3
+ module Volt
4
+ module RepoCache
5
+ class ModelArray
6
+ include Volt::RepoCache::Util
7
+
8
+ def initialize(observer: nil, contents: nil)
9
+ @contents = Volt::ReactiveArray.new(contents || [])
10
+ @id_hash = {}
11
+ @contents.each do |e|
12
+ @id_hash[e.id] = e
13
+ end
14
+ end
15
+
16
+ # subclasses may override if interested.
17
+ def observe(action, model)
18
+ # no op
19
+ end
20
+
21
+ def index(*args, &block)
22
+ @contents.index(*args, &block)
23
+ end
24
+
25
+ def to_a
26
+ # not sure what reactive array does
27
+ # so map contents into normal array
28
+ @contents.map{|e|e}
29
+ end
30
+
31
+ def size
32
+ @contents.size
33
+ end
34
+
35
+ def empty?
36
+ @contents.empty?
37
+ end
38
+
39
+ def detect(*args, &block)
40
+ @contents.detect(*args, &block)
41
+ end
42
+
43
+ def each(&block)
44
+ @contents.each(&block)
45
+ end
46
+
47
+ def each_with_index(&block)
48
+ @contents.each_with_index(&block)
49
+ end
50
+
51
+ def first
52
+ @contents.first
53
+ end
54
+
55
+ def last
56
+ @contents.last
57
+ end
58
+
59
+ def count(&block)
60
+ @contents.count(&block)
61
+ end
62
+
63
+ def sort(&block)
64
+ @contents.sort(&block)
65
+ end
66
+
67
+ def select(&block)
68
+ @contents.select(&block)
69
+ end
70
+
71
+ def reject(&block)
72
+ @contents.reject(&block)
73
+ end
74
+
75
+ def collect(&block)
76
+ @contents.collect(&block)
77
+ end
78
+
79
+ alias_method :map, :collect
80
+
81
+ def reduce(seed, &block)
82
+ @contents.reduce(seed, &block)
83
+ end
84
+
85
+ alias_method :inject, :reduce
86
+
87
+ # Query is simple for now:
88
+ # - a hash of keys and values to match by equality
89
+ # - or a select block
90
+ # TODO: would prefer a splat to the hash,
91
+ # but Opal fails to parse calls with **splats
92
+ def query(args = nil, &block)
93
+ if args.nil? || args.empty?
94
+ if block
95
+ select &block
96
+ else
97
+ raise ArgumentError, 'query requires splat of key-value pairs, or a select block'
98
+ end
99
+ elsif args.size == 1
100
+ k, v = args.first
101
+ if k == :id
102
+ [@id_hash[v]]
103
+ else
104
+ select {|e| e.send(k) == v}
105
+ end
106
+ else
107
+ query do |e|
108
+ match = true
109
+ args.each do |k, v|
110
+ unless e.send(k) == v
111
+ match = false
112
+ break
113
+ end
114
+ end
115
+ match
116
+ end
117
+ end
118
+ end
119
+
120
+ alias_method :where, :query
121
+
122
+ private
123
+
124
+ def __delete_at__(index, notify: true)
125
+ model = @contents.delete_at(index)
126
+ if model
127
+ @id_hash.delete(model.id)
128
+ observe(:remove, model) if notify
129
+ end
130
+ model
131
+ end
132
+
133
+ def __delete__(model, notify: true)
134
+ i = find_index(model)
135
+ __delete_at__(i, notify: notify) if i
136
+ end
137
+
138
+ def __clear__
139
+ @contents.clear
140
+ @id_hash.clear
141
+ end
142
+
143
+ def __remove_if_present__
144
+ __remove__(model, error_if_absent: false)
145
+ end
146
+
147
+ def __remove__(model, error_if_absent: true)
148
+ index = index {|e| e.id == model.id }
149
+ if index
150
+ result = __delete_at__(index)
151
+ # debug __method__, __LINE__, "deleted #{result.class.name} #{result.id}"
152
+ result
153
+ elsif error_if_absent
154
+ msg = "could not find #{model.class.name} with id #{model.id} to delete"
155
+ # debug __method__, __LINE__, msg
156
+ raise RuntimeError, msg
157
+ end
158
+ end
159
+
160
+ def __append__(model, notify: true)
161
+ @contents.append(model)
162
+ @id_hash[model.id] = model
163
+ observe(:add, model) if notify
164
+ self
165
+ end
166
+
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,78 @@
1
+ module Volt
2
+ module RepoCache
3
+ module Util
4
+ module_function
5
+
6
+ def setter(getter)
7
+ :"#{getter}="
8
+ end
9
+
10
+ def creator(getter)
11
+ prefix_method(getter, 'new')
12
+ end
13
+
14
+ def adder(getter)
15
+ prefix_method(getter, 'add')
16
+ end
17
+
18
+ def remover(getter)
19
+ prefix_method(getter, 'remove')
20
+ end
21
+
22
+ def prefix_method(getter, prefix)
23
+ :"#{prefix}_#{getter.to_s.singularize}"
24
+ end
25
+
26
+ def arrify(object)
27
+ object.respond_to?(:to_a) ? object.to_a : [object]
28
+ end
29
+
30
+ def unsupported(method)
31
+ fail "#{method} is an unsupported operation for #{self.class.name}"
32
+ end
33
+
34
+ def not_yet_implemented(method)
35
+ fail "#{method} is not yet implemented for #{self.class.name}"
36
+ end
37
+
38
+ def subclass_responsibility(method)
39
+ fail "#{method} is responsibility of #{self.class.name}"
40
+ end
41
+
42
+ def friends_only(__method__, caller)
43
+ unless friend?(caller)
44
+ fail "#{self.class.name}##{__method__} for Volt::RepoCache use only, not #{caller.class.name}"
45
+ end
46
+ end
47
+
48
+ def friend?(object)
49
+ if object
50
+ if object.is_a?(Volt::Model)
51
+ object.respond_to?(:patched_for_cache?)
52
+ else
53
+ (object.class.name =~ /^Volt::RepoCache/) == 0
54
+ end
55
+ else
56
+ false
57
+ end
58
+ end
59
+
60
+ def debug(method, line, msg = nil)
61
+ s = ">>> #{self.class.name}##{method}[#{line}] : #{msg}"
62
+ if RUBY_PLATFORM == 'opal'
63
+ Volt.logger.debug s
64
+ else
65
+ puts s
66
+ end
67
+ end
68
+
69
+ def time(method, line, msg = nil)
70
+ t1 = Time.now
71
+ r = yield
72
+ t2 = Time.now
73
+ debug(method, line, "#{msg} : took #{t2 - t1} seconds")
74
+ r
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ module Volt
2
+ module RepoCache
3
+ VERSION = "0.1.4"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ .bundle
2
+ .config
3
+ .yardoc
4
+ tmp
5
+ .idea
6
+ .yardoc
7
+ .sass-cache
8
+ .DS_Store
9
+ compiled
@@ -0,0 +1,4 @@
1
+ # Place your app's docs here.
2
+
3
+ ## New to Volt?
4
+ Be sure to read the volt docs at http://voltframework.com/docs
@@ -0,0 +1 @@
1
+ // Place your apps css here
@@ -0,0 +1,11 @@
1
+ # Specify which components you wish to include when
2
+ # the "home" component loads.
3
+
4
+ # bootstrap css framework
5
+ component 'bootstrap'
6
+
7
+ # a default theme for the bootstrap framework
8
+ component 'bootstrap_jumbotron_theme'
9
+
10
+ # provides templates for login, signup, and logout
11
+ component 'user_templates'
@@ -0,0 +1,10 @@
1
+ # Place any code you want to run when the component is included on the client
2
+ # or server.
3
+
4
+ # To include code only on the client use:
5
+ # if RUBY_PLATFORM == 'opal'
6
+ #
7
+ # To include code only on the server, use:
8
+ # unless RUBY_PLATFORM == 'opal'
9
+ # ^^ this will not send compile in code in the conditional to the client.
10
+ # ^^ this include code required in the conditional.
@@ -0,0 +1,14 @@
1
+ # See https://github.com/voltrb/volt#routes for more info on routes
2
+
3
+ client '/about', action: 'about'
4
+
5
+ # Routes for login and signup, provided by user_templates component gem
6
+ client '/signup', component: 'user_templates', controller: 'signup'
7
+ client '/login', component: 'user_templates', controller: 'login', action: 'index'
8
+ client '/password_reset', component: 'user_templates', controller: 'password_reset', action: 'index'
9
+ client '/forgot', component: 'user_templates', controller: 'login', action: 'forgot'
10
+ client '/account', component: 'user_templates', controller: 'account', action: 'index'
11
+
12
+ # The main route, this should be last. It will match any params not
13
+ # previously matched.
14
+ client '/', {}
@@ -0,0 +1,27 @@
1
+ # By default Volt generates this controller for your Main component
2
+ module Main
3
+ class MainController < Volt::ModelController
4
+ def index
5
+ # Add code for when the index view is loaded
6
+ end
7
+
8
+ def about
9
+ # Add code for when the about view is loaded
10
+ end
11
+
12
+ private
13
+
14
+ # The main template contains a #template binding that shows another
15
+ # template. This is the path to that template. It may change based
16
+ # on the params._component, params._controller, and params._action values.
17
+ def main_path
18
+ "#{params._component || 'main'}/#{params._controller || 'main'}/#{params._action || 'index'}"
19
+ end
20
+
21
+ # Determine if the current nav component is the active one by looking
22
+ # at the first part of the url against the href attribute.
23
+ def active_tab?
24
+ url.path.split('/')[1] == attrs.href.split('/')[1]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ class Customer < Volt::Model
2
+ field :name
3
+ has_many :orders
4
+ end
@@ -0,0 +1,6 @@
1
+ class Order < Volt::Model
2
+ field :date, String #YYYYMMDD
3
+ field :quantity, Fixnum
4
+ belongs_to :customer
5
+ belongs_to :product
6
+ end
@@ -0,0 +1,5 @@
1
+ class Product < Volt::Model
2
+ field :name, String
3
+ has_many :orders
4
+ end
5
+
@@ -0,0 +1,12 @@
1
+ # By default Volt generates this User model which inherits from Volt::User,
2
+ # you can rename this if you want.
3
+ class User < Volt::User
4
+ # login_field is set to :email by default and can be changed to :username
5
+ # in config/app.rb
6
+ field login_field
7
+ field :name
8
+
9
+ validate login_field, unique: true, length: 8
10
+ validate :email, email: true
11
+
12
+ end
@@ -0,0 +1,7 @@
1
+ <:Title>
2
+ About
3
+
4
+ <:Body>
5
+ <h1>About</h1>
6
+
7
+ <p>About page...</p>
@@ -0,0 +1,6 @@
1
+ <:Title>
2
+ Home
3
+
4
+ <:Body>
5
+ <h1>Home</h1>
6
+
@@ -0,0 +1,29 @@
1
+ <:Title>
2
+ {{ view main_path, "title", {controller_group: 'main'} }}
3
+
4
+ <:Body>
5
+ <div class="container">
6
+ <div class="header">
7
+ <ul class="nav nav-pills pull-right">
8
+ <:nav href="/">Home</:nav>
9
+ <:nav href="/about">About</:nav>
10
+ <:user_templates:menu />
11
+ </ul>
12
+ <h3 class="text-muted">dummy</h3>
13
+ </div>
14
+
15
+ <:volt:notices />
16
+
17
+ {{ view main_path, 'body', {controller_group: 'main'} }}
18
+
19
+ <div class="footer">
20
+ <p>&copy; Company {{ Time.now.year }}</p>
21
+ </div>
22
+
23
+ </div>
24
+
25
+ <:Nav>
26
+ <li class="{{ if active_tab? }}active{{ end }}">
27
+ <a href="{{ attrs.href }}">{{ yield }}</a>
28
+ </li>
29
+
@@ -0,0 +1,4 @@
1
+ # Run via rack server
2
+ require 'bundler/setup'
3
+ require 'volt/server'
4
+ run Volt::Server.new.app
@@ -0,0 +1,147 @@
1
+ # app.rb is used to configure your app. This code is only run on the server,
2
+ # then any config options in config.public are passed to the client as well.
3
+
4
+ Volt.configure do |config|
5
+ # Setup your global app config here.
6
+
7
+ #######################################
8
+ # Basic App Info (stuff you should set)
9
+ #######################################
10
+ config.domain = 'dummy.com'
11
+ config.app_name = 'Dummy'
12
+ config.mailer.from = 'Dummy <no-reply@dummy.com>'
13
+
14
+ ############
15
+ # App Secret
16
+ ############
17
+ # Your app secret is used for signing things like the user cookie so it can't
18
+ # be tampered with. A random value is generated on new projects that will work
19
+ # without the need to customize. Make sure this value doesn't leave your server.
20
+ #
21
+ # For added security we recommend moving the app secret into an environment. You can
22
+ # setup that like so:
23
+ #
24
+ # config.app_secret = ENV['APP_SECRET']
25
+ #
26
+ config.app_secret = 'BidFdMW1H8fjB7icltOfGImr5G9AHOTCqgnzfZHYnVg5wgPJUS3kdekVRszLYjVmJxw'
27
+
28
+ ###############
29
+ # Log Filtering
30
+ ###############
31
+ # Data updates from the client come in via Tasks. The task dispatcher logs all calls to tasks.
32
+ # By default hashes in the arguments can be filtered based on keys. So any hash with a key of
33
+ # password will be filtered. You can add more fields to filter below:
34
+ config.filter_keys = [:password]
35
+
36
+ ##########
37
+ # Database
38
+ ##########
39
+ # Database config all start with db_ and can be set either in the config
40
+ # file or with an environment variable (DB_NAME for example).
41
+
42
+ config.db_driver = 'mongo'
43
+ config.db_name = (config.app_name + '_' + Volt.env.to_s)
44
+ config.db_host = 'localhost'
45
+ config.db_port = 27017
46
+
47
+ #####################
48
+ # Compression options
49
+ #####################
50
+ # If you are not running behind something like nginx in production, you can
51
+ # have rack deflate all files.
52
+ # config.deflate = true
53
+
54
+ #########################
55
+ # Websocket configuration
56
+ #########################
57
+ # If you need to use a different domain or path for the websocket connection,
58
+ # you can set it here. Volt provides the socket connection url at /socket,
59
+ # but if for example you are using a proxy server that doesn't support
60
+ # websockets, you can point the websocket connection at the app server
61
+ # directly.
62
+ # config.public.websocket_url = '/socket'
63
+
64
+ #######################
65
+ # Public configurations
66
+ #######################
67
+ # Anything under config.public will be sent to the client as well as the server,
68
+ # so be sure no private data ends up under public
69
+
70
+ # Use username instead of email as the login
71
+ # config.public.auth.use_username = true
72
+
73
+ #####################
74
+ # Compression Options
75
+ #####################
76
+ # Disable or enable css/js/image compression. Default is to only run in production.
77
+ # if Volt.env.production?
78
+ # config.compress_javascript = true
79
+ # config.compress_css = true
80
+ # config.compress_images = true
81
+ # end
82
+
83
+ ################
84
+ # Mailer options
85
+ ################
86
+ # The volt-mailer gem uses pony (https://github.com/benprew/pony) to deliver e-mail. Any
87
+ # options you would pass to pony can be setup below.
88
+ # NOTE: The from address is setup at the top
89
+
90
+ # Normally pony uses /usr/sbin/sendmail if one is installed. You can specify smtp below:
91
+ # config.mailer.via = :smtp
92
+ # config.mailer.via_options = {
93
+ # :address => 'smtp.yourserver.com',
94
+ # :port => '25',
95
+ # :user_name => 'user',
96
+ # :password => 'password',
97
+ # :authentication => :plain, # :plain, :login, :cram_md5, no auth by default
98
+ # :domain => "localhost.localdomain" # the HELO domain provided by the client to the server
99
+ # }
100
+
101
+ #############
102
+ # Message Bus
103
+ #############
104
+ # Volt provides a "Message Bus" out of the box. The message bus provides
105
+ # a pub/sub service between any volt instance (server, client, runner, etc..)
106
+ # that share the same database. The message bus can be used by app code. It
107
+ # is also used internally to push data to any listening clients.
108
+ #
109
+ # The default message bus (called "peer_to_peer") uses the database to sync
110
+ # socket ip's/ports.
111
+ # config.message_bus.bus_name = 'peer_to_peer'
112
+ #
113
+ # Encrypt message bus - messages on the message bus are encrypted by default
114
+ # using rbnacl.
115
+
116
+ #
117
+ # For dummy apps, we disable_encryption, to simplify the gem requirements.
118
+ config.message_bus.disable_encryption = true
119
+
120
+ #
121
+ # ## MessageBus Server -- the message bus binds to a port and ip which the
122
+ # other volt instances need to be able to connect to. You can customize
123
+ # the server below:
124
+ #
125
+ # Port range - you can specify a range of ports that an instance can bind the
126
+ # message bus on. You can specify a range, an array of Integers, or an array
127
+ # of ranges.
128
+ # config.message_bus.bind_port_ranges = (58000..61000)
129
+ #
130
+ # Bind Ip - specifies the ip address the message bus server should bind on.
131
+ # config.message_bus.bind_ip = '127.0.0.1'
132
+
133
+ #############
134
+ # Concurrency
135
+ #############
136
+ # Volt provides a thread worker pool for incoming task requests (and all
137
+ # database requests, since those use tasks to do their work.) The following
138
+ # lets you control the size of the worker pool. Threads are only created as
139
+ # needed, and are removed after a certain amount of inactivity.
140
+ # config.min_worker_threads = 1
141
+ # config.max_worker_threads = 10
142
+ #
143
+ # You can also specify the amount of time a Task should run for before it
144
+ # timeout's. Setting this to short can cause unexpected results, currently
145
+ # we recomend it be at least 10 seconds.
146
+ # config.worker_timeout = 60
147
+ end