volt-repo_cache 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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