Skip to content

Commit

Permalink
- change model listener remove delay to 5s on client
Browse files Browse the repository at this point in the history
- improve error logging for bindings
- fix peer_to_peer migration issue
- Volt.current_user works in HttpController’s
- Volt.current_user returns a promise, (deprecate fetch_current_user)
  • Loading branch information
ryanstout committed Jun 3, 2015
1 parent 719a78a commit eefbbf6
Show file tree
Hide file tree
Showing 30 changed files with 280 additions and 163 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
- You can now nest models on store. Previously store was limited to only storing either values or ArrayModels (associations). You can now store directly, in mongo this will be stored as a nested value.
- Promises got more awesome. Promises in volt can now proxy methods to their future resolved value. Something like: ```promise.then {|v| v.name }``` can now be written simply as: ```promise.name``` It will still return a promise, but to make life easier:
- All bindings now support promises directly.
- You can now set/get properties directly on ```store``` and they will be saved to a ```root_stores_models``` table.
- All code in config/initializers is now run on app startup.
- All code in any components config/initializers (app/main/config/initializers/*.rb) is now run on the server during app startup. On the client, only the included components initializers will be run.
- all initializers folders now support a ```client``` and ```server``` folder.
- has_one is now supported.
- You can now use .create to make a new item on a collection.
- .inspect for models is now cleaner
- Volt.current_user now works in HttpController's

### Changed
- All methods on ArrayModel's under the store collection now return a Promise.
Expand All @@ -30,6 +31,8 @@
- You can now use .each in attribute bindings.
- We moved to csso as the css compressor because it does not require libv8, only an execjs runtime.
- Each bindings now support promises.
- Volt.fetch_current_user has been deprecated and Volt.current_user now returns a promise.
- Volt.current_user? now returns a promise that yields a boolean

## 0.9.2
### Changed
Expand Down
5 changes: 5 additions & 0 deletions app/volt/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

module Volt
class User < Model
field :username
field :email
field :name
field :password

# returns login field name depending on config settings
def self.login_field
if Volt.config.try(:public).try(:auth).try(:use_username)
Expand Down
1 change: 1 addition & 0 deletions lib/volt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require 'volt/utils/modes'
require 'volt/utils/volt_user_error'
require 'volt/utils/boolean_patch'
require 'volt/utils/set_patch'

require 'volt/config'
require 'volt/data_stores/data_store' unless RUBY_PLATFORM == 'opal'
Expand Down
4 changes: 4 additions & 0 deletions lib/volt/cli/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ def component(name)
def gem(name)
require 'volt/cli/new_gem'

# remove prefixed volt-
name = name.gsub(/^volt[-]/, '')

if name =~ /[-]/
require 'volt/extra_core/logger'
Volt.logger.error('Gem names should use underscores for their names. Currently volt only supports a single namespace for a component.')
return
end
Expand Down
2 changes: 1 addition & 1 deletion lib/volt/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
require 'volt/models/persistors/flash'
require 'volt/models/persistors/local_store'
require 'volt/models/root_models/root_models'
require 'volt/models/root_models/store_root'
# require 'volt/models/root_models/store_root'

# Fow now, we're keeping a volt copy of the promise library from opal 0.8,
# since opal 0.7.x's version has some bugs.
Expand Down
8 changes: 6 additions & 2 deletions lib/volt/models/array_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ def delete(val)
end

def first
self[0]
if persistor.is_a?(Persistors::ArrayStore)
limit(1)[0]
else
self[0]
end
end

# Return the first item in the collection, or create one if one does not
Expand Down Expand Up @@ -262,7 +266,7 @@ def buffer(attrs = {})
end

# We need to setup the proxy methods below where they are defined.
proxy_with_load :first, :[], :size, :last, :reverse, :all, :to_a
proxy_with_load :[], :size, :last, :reverse, :all, :to_a

end
end
2 changes: 1 addition & 1 deletion lib/volt/models/persistors/array_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def listener_removed
# sync with the database. The data is kept in memory and the model's
# loaded_state is marked as "dirty" meaning it may not be in sync.
def stop_listening
Timers.next_tick do
Timers.client_set_timeout(5000) do
Computation.run_without_tracking do
if @listener_event_counter.count == 0
if @added_to_query
Expand Down
21 changes: 17 additions & 4 deletions lib/volt/models/root_models/store_root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,41 @@
module Volt
module StoreRootHelpers
def model_for_root
root = get(:root_store_models).first_or_create
root = nil
Volt::Computation.run_without_tracking do
root = get(:root_store_models).first_or_create
end

root
end


def get(attr_name, expand = false)
if attr_name.singular? && attr_name.to_sym != :id
res = if attr_name.singular? && attr_name.to_sym != :id
puts "GET: #{attr_name}"
model_for_root.get(attr_name, expand)
else
super
end

# puts "GOT: #{res.inspect}"
res
end

def set(attr_name, value, &block)
if attr_name.singular? && attr_name.to_sym != :id
model_for_root.set(attr_name, value, &block)
puts "SET ATTR NAME: #{attr_name.inspect}: #{value.inspect}"
Volt::Computation.run_without_tracking do
model_for_root.then do |model|
model.set(attr_name, value, &block)
end
end
else
super
end
# puts "SET---"
end
end
end

StoreRoot.send(:include, Volt::StoreRootHelpers)
# StoreRoot.send(:include, Volt::StoreRootHelpers)
2 changes: 1 addition & 1 deletion lib/volt/models/validators/unique_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def self.validate(model, field_name, args)

# Check if the value is taken
# TODO: need a way to handle scope for unique
return $page.store.get(model.path[-2]).where(query).first do |item|
return $page.store.get(model.path[-2]).where(query).first.then do |item|
if item
message = (args.is_a?(Hash) && args[:message]) || 'is already taken'

Expand Down
9 changes: 5 additions & 4 deletions lib/volt/page/bindings/attribute_binding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ def setup
begin
@context.instance_eval(&@getter)
rescue => e
Volt.logger.error("AttributeBinding Error: #{e.inspect}")
getter_fail(e)
''
end
end.watch_and_resolve! do |result|
update(result)
end
end.watch_and_resolve!(
method(:update),
method(:getter_fail)
)

@is_select = `#{element}.is('select')`
@is_hidden = `#{element}.is('[type=hidden]')`
Expand Down
17 changes: 17 additions & 0 deletions lib/volt/page/bindings/base_binding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,22 @@ def remove
def remove_anchors
@dom_section.remove_anchors if @dom_section
end

# log out a message about a failed computation or Promise.
def getter_fail(error)
message = "#{self.class.to_s} Error: #{error.inspect}"

if RUBY_PLATFORM == 'opal'
if `#{@getter}`
message += "\n" + `#{@getter}.toString()`
end
else
if error.respond_to?(:backtrace)
message += "\n" + error.backtrace.join("\n")
end
end

Volt.logger.error(message)
end
end
end
12 changes: 7 additions & 5 deletions lib/volt/page/bindings/content_binding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@ class ContentBinding < BaseBinding

def initialize(volt_app, target, context, binding_name, getter)
super(volt_app, target, context, binding_name)
@getter = getter

# Listen for changes
@computation = lambda do
begin
res = @context.instance_eval(&getter)
@context.instance_eval(&getter)
rescue => e
Volt.logger.error("ContentBinding Error: #{e.inspect}")
getter_fail(e)
''
end
end.watch_and_resolve! do |result|
update(result)
end
end.watch_and_resolve!(
method(:update),
method(:getter_fail)
)
end

def update(value)
Expand Down
113 changes: 62 additions & 51 deletions lib/volt/page/bindings/each_binding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@ def initialize(volt_app, target, context, binding_name, getter, variable_name, i
value = @context.instance_eval(&@getter)
rescue => e
Volt.logger.error("EachBinding Error: #{e.inspect}")
if RUBY_PLATFORM == 'opal'
Volt.logger.error(`#{@getter}`)
else
Volt.logger.error(e.backtrace.join("\n"))
end

value = []
end

value
end.watch_and_resolve! do |value|
update(value)
end
end.watch_and_resolve!(
method(:update),
method(:getter_fail)
)
end

# When a changed event happens, we update to the new size.
Expand Down Expand Up @@ -64,69 +71,73 @@ def update(value)
end

def item_removed(position)
Volt.run_in_mode(:no_model_promises) do
# Remove dependency
@templates[position].context.locals[:_index_dependency].remove
@templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove
# Remove dependency
@templates[position].context.locals[:_index_dependency].remove
@templates[position].context.locals["_#{@item_name}_dependency".to_sym].remove

@templates[position].remove_anchors
@templates[position].remove
@templates.delete_at(position)
@templates[position].remove_anchors
@templates[position].remove
@templates.delete_at(position)

# Removed at the position, update context for every item after this position
update_indexes_after(position)
end
# Removed at the position, update context for every item after this position
update_indexes_after(position)
end

def item_added(position)
Volt.run_in_mode(:no_model_promises) do
binding_name = @@binding_number
@@binding_number += 1

if position >= @templates.size
# Setup new bindings in the spot we want to insert the item
dom_section.insert_anchor_before_end(binding_name)
else
# Insert the item before an existing item
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
end
item_context = nil

# TODORW: :parent => @value may change
item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
item_context.locals[@item_name.to_sym] = proc { @value[item_context.locals[:_index_value]] }
binding_name = @@binding_number
@@binding_number += 1

position_dependency = Dependency.new
item_context.locals[:_index_dependency] = position_dependency
if position >= @templates.size
# Setup new bindings in the spot we want to insert the item
dom_section.insert_anchor_before_end(binding_name)
else
# Insert the item before an existing item
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
end

# Get and set index
item_context.locals[:_index=] = proc do |val|
position_dependency.changed!
item_context.locals[:_index_value] = val
# TODORW: :parent => @value may change
item_context = SubContext.new({ _index_value: position, parent: @value }, @context)
item_context.locals[@item_name.to_sym] = proc do
# Fetch only whats there currently, no promises.
Volt.run_in_mode(:no_model_promises) do
# puts "GET AT: #{item_context.locals[:_index_value]}"
@value[item_context.locals[:_index_value]]
end
end

# Get and set value
value_dependency = Dependency.new
item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency
position_dependency = Dependency.new
item_context.locals[:_index_dependency] = position_dependency

item_context.locals["#{@item_name}=".to_sym] = proc do |val|
value_dependency.changed!
@value[item_context.locals[:_index_value]] = val
end
# Get and set index
item_context.locals[:_index=] = proc do |val|
position_dependency.changed!
item_context.locals[:_index_value] = val
end

# If the user provides an each_with_index, we can assign the lookup for the index
# variable here.
if @index_name
item_context.locals[@index_name.to_sym] = proc do
position_dependency.depend
item_context.locals[:_index_value]
end
end
# Get and set value
value_dependency = Dependency.new
item_context.locals["_#{@item_name}_dependency".to_sym] = value_dependency

item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
@templates.insert(position, item_template)
item_context.locals["#{@item_name}=".to_sym] = proc do |val|
value_dependency.changed!
@value[item_context.locals[:_index_value]] = val
end

update_indexes_after(position)
# If the user provides an each_with_index, we can assign the lookup for the index
# variable here.
if @index_name
item_context.locals[@index_name.to_sym] = proc do
position_dependency.depend
item_context.locals[:_index_value]
end
end

item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
@templates.insert(position, item_template)

update_indexes_after(position)
end

# When items are added or removed in the middle of the list, we need
Expand Down
Loading

0 comments on commit eefbbf6

Please sign in to comment.