bowline 0.5.8 → 0.6.0

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 (46) hide show
  1. data/README.txt +2 -2
  2. data/Rakefile +3 -2
  3. data/TODO +2 -0
  4. data/VERSION +1 -1
  5. data/assets/bowline.js +131 -68
  6. data/assets/bowline.test.js +86 -0
  7. data/bowline.gemspec +15 -17
  8. data/examples/tweet.rb +3 -1
  9. data/lib/bowline.rb +9 -5
  10. data/lib/bowline/app_config.rb +41 -0
  11. data/lib/bowline/binders.rb +70 -101
  12. data/lib/bowline/binders/collection.rb +37 -0
  13. data/lib/bowline/binders/observer.rb +30 -0
  14. data/lib/bowline/binders/singleton.rb +43 -0
  15. data/lib/bowline/commands/run.rb +1 -0
  16. data/lib/bowline/desktop/bridge.rb +4 -4
  17. data/lib/bowline/desktop/js.rb +7 -3
  18. data/lib/bowline/desktop/runtime.rb +29 -0
  19. data/lib/bowline/desktop/window.rb +9 -1
  20. data/lib/bowline/desktop/window_methods.rb +1 -1
  21. data/lib/bowline/generators/application.rb +1 -0
  22. data/lib/bowline/generators/binder.rb +7 -2
  23. data/lib/bowline/generators/model.rb +1 -1
  24. data/lib/bowline/helpers.rb +2 -0
  25. data/lib/bowline/initializer.rb +39 -87
  26. data/lib/bowline/library.rb +1 -7
  27. data/lib/bowline/tasks/app.rake +1 -53
  28. data/lib/bowline/tasks/libs.rake +4 -17
  29. data/lib/bowline/version.rb +2 -2
  30. data/lib/bowline/watcher.rb +50 -23
  31. data/templates/Gemfile +10 -0
  32. data/templates/config/boot.rb +11 -8
  33. data/templates/main_window.rb +1 -0
  34. metadata +20 -15
  35. data/lib/bowline/dependencies/FAQ.markdown +0 -6
  36. data/lib/bowline/dependencies/MIT-LICENSE +0 -20
  37. data/lib/bowline/dependencies/README.markdown +0 -51
  38. data/lib/bowline/dependencies/TODO.markdown +0 -4
  39. data/lib/bowline/dependencies/lib/dependencies.rb +0 -6
  40. data/lib/bowline/dependencies/lib/dependencies/dependency.rb +0 -12
  41. data/lib/bowline/dependencies/lib/dependencies/repository.rb +0 -64
  42. data/lib/bowline/dependencies/lib/ext/rubygems.rb +0 -116
  43. data/lib/bowline/ext/class.rb +0 -51
  44. data/lib/bowline/ext/string.rb +0 -9
  45. data/lib/bowline/local_model.rb +0 -142
  46. data/lib/bowline/tasks/gems.rake +0 -36
@@ -1,4 +1,6 @@
1
- class Tweet < Bowline::LocalModel
1
+ require "supermodel"
2
+
3
+ class Tweet < SuperModel::Base
2
4
  class << self
3
5
  def poll
4
6
  destroy_all
@@ -1,3 +1,7 @@
1
+ require 'active_support'
2
+ require 'active_support/dependencies'
3
+ require 'active_support/core_ext/string/access'
4
+
1
5
  Thread.abort_on_exception = true
2
6
 
3
7
  module Bowline
@@ -24,12 +28,9 @@ require 'bowline/version'
24
28
 
25
29
  require 'bowline/ext/object'
26
30
  require 'bowline/ext/array'
27
- require 'bowline/ext/class'
28
- require 'bowline/ext/string'
29
31
 
30
32
  require 'bowline/logging'
31
33
  require 'bowline/watcher'
32
- require 'bowline/local_model'
33
34
 
34
35
  require 'bowline/platform'
35
36
  require 'bowline/library'
@@ -52,7 +53,10 @@ require 'bowline/desktop/bridge'
52
53
  require 'bowline/desktop/window_manager.rb'
53
54
 
54
55
  require 'bowline/helpers'
55
- require 'bowline/dependencies/lib/dependencies'
56
+ require 'bowline/app_config'
56
57
  require 'bowline/initializer'
57
58
 
58
- require 'bowline/binders'
59
+ require 'bowline/binders'
60
+ require 'bowline/binders/observer'
61
+ require 'bowline/binders/collection'
62
+ require 'bowline/binders/singleton'
@@ -0,0 +1,41 @@
1
+ module Bowline
2
+ class AppConfig
3
+ attr_reader :keys, :path
4
+ def initialize(path)
5
+ @path = path
6
+ @keys = {}
7
+ load!
8
+ end
9
+
10
+ def load!
11
+ return unless File.exist?(path)
12
+ @keys = YAML::load(File.read(path))
13
+ end
14
+
15
+ def dump!
16
+ File.open(path, "w+") {|f| f.write(YAML::dump(keys)) }
17
+ end
18
+
19
+ def delete(key)
20
+ @keys.delete(key)
21
+ dump!
22
+ end
23
+
24
+ def method_missing(sym, *args)
25
+ method_name = sym.to_s
26
+
27
+ if method_name =~ /(=|\?)$/
28
+ case $1
29
+ when "="
30
+ keys[$`] = args.first
31
+ dump!
32
+ when "?"
33
+ keys[$`]
34
+ end
35
+ else
36
+ return keys[method_name] if keys.include?(method_name)
37
+ super
38
+ end
39
+ end
40
+ end
41
+ end
@@ -65,11 +65,13 @@ module Bowline
65
65
  def windows
66
66
  @windows ||= []
67
67
  end
68
-
68
+
69
69
  def setup(window) #:nodoc:
70
70
  self.windows << window
71
- items = initial
72
- items.try(:to_js) || []
71
+ if initial_items = initial
72
+ self.items = initial_items
73
+ end
74
+ Hash.new # Empty options
73
75
  end
74
76
 
75
77
  # Called by a window's JavaScript whenever that window is bound to this Binder.
@@ -80,7 +82,6 @@ module Bowline
80
82
  # klass.all(:limit => 10)
81
83
  # end
82
84
  def initial
83
- klass.all
84
85
  end
85
86
 
86
87
  def js_invoke(window, method, *args) #:nodoc:
@@ -104,7 +105,7 @@ module Bowline
104
105
 
105
106
  # Set the binder's items. This will replace all items, and update the HTML.
106
107
  def items=(items)
107
- bowline.populate(name, items.to_js).call
108
+ bowline.replace(name, items.to_js).call
108
109
  end
109
110
 
110
111
  # Add a new item to the binder, updating the HTML.
@@ -138,103 +139,71 @@ module Bowline
138
139
  item.id
139
140
  ).call
140
141
  end
141
-
142
- protected
143
- # Associate the binder with a model to setup callbacks so
144
- # changes to the model are automatically reflected in the view.
145
- # Example:
146
- # bind Post
147
- #
148
- # When the bound class is created/updated/deleted
149
- # the binder's callbacks are executed and the view
150
- # updated accordingly.
151
- #
152
- # Classes inheriting fromActiveRecord and Bowline::LocalModel are
153
- # automatically compatable, but if you're using your own custom model
154
- # you need to make sure it responds to the following methods:
155
- # * all - return all records
156
- # * find(id) - find record by id
157
- # * after_create(method) - after_create callback
158
- # * after_update(method) - after_update callback
159
- # * after_destroy(method) - after_destroy callback
160
- #
161
- # The klass' instance needs to respond to:
162
- # * id - returns record id
163
- # * to_js - return record's attribute hash
164
- #
165
- # You can override the to_js method on the model instance
166
- # in order to return specific attributes for the view.
167
- def bind(klass)
168
- @klass = klass
169
- @klass.after_create(method(:created))
170
- @klass.after_update(method(:updated))
171
- @klass.after_destroy(method(:removed))
172
- end
173
-
174
- # Returns class set by the 'bind' method
175
- def klass
176
- @klass || raise("klass not set - see bind method")
177
- end
178
-
179
- # JavaScript proxy to the page.
180
- # See Bowline::Desktop::Proxy for more information.
181
- # Example:
182
- # page.myFunc(1,2,3).call
183
- def page
184
- Bowline::Desktop::Proxy.new(
185
- windows.length == 1 ? windows.first : windows
186
- )
187
- end
188
-
189
- # JavaScript proxy to the Bowline object.
190
- # See Bowline::Desktop::Proxy for more information.
191
- # Example:
192
- # bowline.log("msg").call
193
- def bowline
194
- page.Bowline
195
- end
142
+
143
+ # Returns class set by the 'bind' method
144
+ def klass
145
+ @klass || raise("klass not set - see bind method")
146
+ end
196
147
 
197
- # Javascript proxy to jQuery.
198
- # See Bowline::Desktop::Proxy for more information.
199
- # Example:
200
- # jquery.getJSON("http://example.com").call
201
- def jquery
202
- page.jQuery
203
- end
204
-
205
- # See Bowline::logger
206
- def logger
207
- Bowline::logger
208
- end
209
-
210
- # Trigger events on all elements
211
- # bound to this binder.
212
- # Example:
213
- # trigger(:reload, {:key => :value})
214
- def trigger(event, data = nil)
215
- bowline.trigger(
216
- name,
217
- format_event(event),
218
- data
219
- ).call
220
- end
221
-
222
- # Helper method to trigger a loading
223
- # event either side of a block:
224
- # loading {
225
- # # Slow code, e.g. http call
226
- # }
227
- def loading(&block)
228
- trigger(:loading, true)
229
- yield
230
- trigger(:loading, false)
231
- end
232
-
233
- def format_event(name) #:nodoc:
234
- name.is_a?(Array) ?
235
- name.join('.') :
236
- name.to_s
237
- end
148
+ # JavaScript proxy to the page.
149
+ # See Bowline::Desktop::Proxy for more information.
150
+ # Example:
151
+ # page.myFunc(1,2,3).call
152
+ def page
153
+ Bowline::Desktop::Proxy.new(
154
+ windows.length == 1 ? windows.first : windows
155
+ )
156
+ end
157
+
158
+ # JavaScript proxy to the Bowline object.
159
+ # See Bowline::Desktop::Proxy for more information.
160
+ # Example:
161
+ # bowline.log("msg").call
162
+ def bowline
163
+ page.Bowline
164
+ end
165
+
166
+ # Javascript proxy to jQuery.
167
+ # See Bowline::Desktop::Proxy for more information.
168
+ # Example:
169
+ # jquery.getJSON("http://example.com").call
170
+ def jquery
171
+ page.jQuery
172
+ end
173
+
174
+ # See Bowline::logger
175
+ def logger
176
+ Bowline::logger
177
+ end
178
+
179
+ # Trigger events on all elements
180
+ # bound to this binder.
181
+ # Example:
182
+ # trigger(:reload, {:key => :value})
183
+ def trigger(event, data = nil)
184
+ bowline.trigger(
185
+ name,
186
+ format_event(event),
187
+ data
188
+ ).call
189
+ end
190
+
191
+ # Helper method to trigger a loading
192
+ # event either side of a block:
193
+ # loading {
194
+ # # Slow code, e.g. http call
195
+ # }
196
+ def loading(&block)
197
+ trigger(:loading, true)
198
+ yield
199
+ trigger(:loading, false)
200
+ end
201
+
202
+ def format_event(name) #:nodoc:
203
+ name.is_a?(Array) ?
204
+ name.join('.') :
205
+ name.to_s
206
+ end
238
207
  end
239
208
 
240
209
  # jQuery element object
@@ -0,0 +1,37 @@
1
+ module Bowline
2
+ module Binders
3
+ class Collection < Base
4
+ class << self
5
+ # Associate the binder with a model to setup callbacks so
6
+ # changes to the model are automatically reflected in the view.
7
+ # Example:
8
+ # bind Post
9
+ #
10
+ # When the bound class is created/updated/deleted
11
+ # the binder's callbacks are executed and the view
12
+ # updated accordingly.
13
+ #
14
+ # Classes inheriting from ActiveRecord and SuperModel are
15
+ # automatically compatible, but if you're using your own custom model
16
+ # you need to make sure it responds to the following methods:
17
+ # * all - return all records
18
+ # * find(id) - find record by id
19
+ # * after_create(method) - after_create callback
20
+ # * after_update(method) - after_update callback
21
+ # * after_destroy(method) - after_destroy callback
22
+ #
23
+ # The klass' instance needs to respond to:
24
+ # * id - returns record id
25
+ # * to_js - return record's attribute hash
26
+ #
27
+ # You can override the to_js method on the model instance
28
+ # in order to return specific attributes for the view.
29
+ def bind(klass)
30
+ @klass = klass
31
+ observer = Observer.new(self)
32
+ @klass.add_observer(observer)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ module Bowline
2
+ module Binders
3
+ class Observer
4
+ attr_reader :binder
5
+ def initialize(binder)
6
+ @binder = binder
7
+ end
8
+
9
+ def after_create(rec)
10
+ binder.created(rec)
11
+ end
12
+
13
+ def after_update(rec)
14
+ binder.updated(rec)
15
+ end
16
+
17
+ def after_destroy(rec)
18
+ binder.removed(rec)
19
+ end
20
+
21
+ def update(observed_method, object) #:nodoc:
22
+ send(observed_method, object) if respond_to?(observed_method)
23
+ end
24
+
25
+ def observed_class_inherited(subclass) #:nodoc:
26
+ subclass.add_observer(self)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ module Bowline
2
+ module Binders
3
+ class Singleton < Base
4
+ class << self
5
+ def setup(*args) #:nodoc
6
+ super(*args)
7
+ {:singleton => true}
8
+ end
9
+
10
+ alias :item= :items=
11
+
12
+ # Associate the binder with a model to setup callbacks so
13
+ # changes to the model are automatically reflected in the view.
14
+ # Example:
15
+ # bind Post
16
+ #
17
+ # When the bound class is updated/deleted
18
+ # the binder's callbacks are executed and the view
19
+ # updated accordingly.
20
+ #
21
+ # Classes inheriting from ActiveRecord and SuperModel are
22
+ # automatically compatable, but if you're using your own custom model
23
+ # you need to make sure it responds to the following methods:
24
+ # * all - return all records
25
+ # * find(id) - find record by id
26
+ # * after_update(method) - after_update callback
27
+ # * after_destroy(method) - after_destroy callback
28
+ #
29
+ # The klass' instance needs to respond to:
30
+ # * id - returns record id
31
+ # * to_js - return record's attribute hash
32
+ #
33
+ # You can override the to_js method on the model instance
34
+ # in order to return specific attributes for the view.
35
+ def bind(klass)
36
+ @klass = klass
37
+ observer = Observer.new(self)
38
+ @klass.add_observer(observer)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,4 +1,5 @@
1
1
  unless Bowline::Library.ready?
2
+ puts "Setting up Bowline. This could take a while..."
2
3
  require 'bowline/tasks/bowline'
3
4
  Rake::Task['libs:setup'].invoke
4
5
  end
@@ -24,7 +24,7 @@ module Bowline
24
24
  # TODO - implement options,
25
25
  # like :except and :only
26
26
  instance_eval <<-RUBY
27
- def js_exposed?
27
+ def js_exposed?(meth)
28
28
  true
29
29
  end
30
30
 
@@ -61,10 +61,10 @@ module Bowline
61
61
  object = klass.constantize
62
62
  end
63
63
  trace "JS invoking: #{klass}.#{method}(#{args.join(',')})"
64
- if object.respond_to?(:js_exposed?) && object.js_exposed?
64
+ if object.respond_to?(:js_exposed?) && object.js_exposed?(method)
65
65
  result = object.js_invoke(window, method, *args)
66
66
  if callback?
67
- proxy = Proxy.new(window)
67
+ proxy = Proxy.new(window)
68
68
  proxy.Bowline.invokeCallback(id, result.to_js.to_json)
69
69
  window.run_script(proxy.to_s)
70
70
  end
@@ -80,7 +80,7 @@ module Bowline
80
80
  result = JSON.parse(str)
81
81
  Message.new(window, result).invoke
82
82
  rescue => e
83
- Bowline::Logging.log_error(e)
83
+ Bowline::Logging.log_error e
84
84
  end
85
85
  module_function :call
86
86
  end
@@ -41,15 +41,18 @@ module Bowline
41
41
  end
42
42
 
43
43
  def parse(str)
44
+ return if str.blank?
44
45
  # This is crazy! The JSON
45
46
  # lib can't parse booleans
46
47
  case str
47
48
  when "true" then true
48
49
  when "false" then false
49
- when nil then nil
50
50
  else
51
51
  JSON.parse(str)
52
52
  end
53
+ rescue => e
54
+ trace "Parsing: #{str}"
55
+ raise e
53
56
  end
54
57
  end
55
58
 
@@ -59,7 +62,7 @@ module Bowline
59
62
  module_function :poll
60
63
 
61
64
  def setup
62
- Desktop.on_tick(method(:poll))
65
+ Desktop.on_idle(method(:poll))
63
66
  end
64
67
  module_function :setup
65
68
 
@@ -76,8 +79,9 @@ module Bowline
76
79
  private
77
80
  def run_scripts
78
81
  ready_scripts = scripts.select(&:ready?)
79
- while script = ready_scripts.shift
82
+ ready_scripts.each do |script|
80
83
  script.call
84
+ scripts.delete(script)
81
85
  end
82
86
  end
83
87
  module_function :run_scripts