bowline 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/README.txt +62 -62
  3. data/Rakefile +10 -0
  4. data/TODO +3 -1
  5. data/VERSION +1 -1
  6. data/assets/bowline.js +118 -3
  7. data/bowline.gemspec +6 -6
  8. data/examples/example.js +2 -15
  9. data/examples/tweet.rb +35 -0
  10. data/examples/tweets_binder.rb +6 -0
  11. data/examples/twitter.html +6 -8
  12. data/examples/users.rb +8 -14
  13. data/lib/bowline/binders.rb +130 -42
  14. data/lib/bowline/dependencies/lib/dependencies.rb +2 -1
  15. data/lib/bowline/dependencies/lib/ext/rubygems.rb +4 -4
  16. data/lib/bowline/desktop.rb +11 -2
  17. data/lib/bowline/desktop/app.rb +7 -3
  18. data/lib/bowline/desktop/bridge.rb +8 -9
  19. data/lib/bowline/desktop/clipboard.rb +11 -2
  20. data/lib/bowline/desktop/dialog.rb +19 -0
  21. data/lib/bowline/desktop/dock.rb +14 -3
  22. data/lib/bowline/desktop/host.rb +13 -4
  23. data/lib/bowline/desktop/js.rb +2 -2
  24. data/lib/bowline/desktop/misc.rb +7 -3
  25. data/lib/bowline/desktop/network.rb +1 -1
  26. data/lib/bowline/desktop/proxy.rb +28 -30
  27. data/lib/bowline/desktop/sound.rb +3 -2
  28. data/lib/bowline/desktop/window.rb +147 -21
  29. data/lib/bowline/desktop/window_manager.rb +53 -5
  30. data/lib/bowline/desktop/window_methods.rb +2 -2
  31. data/lib/bowline/ext/object.rb +1 -1
  32. data/lib/bowline/generators.rb +1 -1
  33. data/lib/bowline/generators/binder.rb +1 -1
  34. data/lib/bowline/helpers.rb +1 -1
  35. data/lib/bowline/initializer.rb +37 -4
  36. data/lib/bowline/library.rb +11 -0
  37. data/lib/bowline/local_model.rb +45 -19
  38. data/lib/bowline/logging.rb +1 -1
  39. data/lib/bowline/platform.rb +8 -6
  40. data/lib/bowline/tasks/app.rake +4 -3
  41. data/lib/bowline/version.rb +1 -1
  42. data/lib/bowline/watcher.rb +18 -8
  43. data/templates/binder.rb +1 -1
  44. data/vendor/pathname.rb +0 -4
  45. metadata +6 -6
  46. data/examples/account.rb +0 -31
  47. data/examples/tweets.rb +0 -28
data/examples/tweet.rb ADDED
@@ -0,0 +1,35 @@
1
+ class Tweet < Bowline::LocalModel
2
+ class << self
3
+ def poll
4
+ destroy_all
5
+ populate(timeline)
6
+ end
7
+
8
+ def timeline
9
+ twitter.friends_timeline.collect {|t|
10
+ t.delete('user')
11
+ t.to_hash
12
+ }
13
+ end
14
+
15
+ def update(status)
16
+ twitter.update(status)
17
+ end
18
+
19
+ private
20
+ def twitter
21
+ httpauth = Twitter::HTTPAuth.new(
22
+ AppConfig.username,
23
+ AppConfig.password
24
+ )
25
+ Twitter::Base.new(httpauth)
26
+ end
27
+ end
28
+ end
29
+
30
+ Thread.new do
31
+ loop do
32
+ Tweet.poll
33
+ sleep 60
34
+ end
35
+ end
@@ -0,0 +1,6 @@
1
+ class TweetsBinder < Bowline::Binders::Base
2
+ bind Tweet
3
+ def self.update(status)
4
+ klass.update(status)
5
+ end
6
+ end
@@ -6,18 +6,16 @@
6
6
  <link rel="stylesheet" href="stylesheets/application.css" type="text/css" charset="utf-8">
7
7
  <script src="javascripts/jquery.js" type="text/javascript" charset="utf-8"></script>
8
8
  <script src="javascripts/jquery.chain.js" type="text/javascript" charset="utf-8"></script>
9
- <script src="javascripts/jquery.bowline.js" type="text/javascript" charset="utf-8"></script>
9
+ <script src="javascripts/json2.js" type="text/javascript" charset="utf-8"></script>
10
+ <script src="javascripts/bowline.js" type="text/javascript" charset="utf-8"></script>
10
11
  <script src="javascripts/application.js" type="text/javascript" charset="utf-8"></script>
11
12
  <script type="text/javascript" charset="utf-8">
12
13
  jQuery(function($){
13
- $.bowline.ready(function(){
14
- var tweets = $('#tweets').bowline('tweets');
15
- tweets.invoke('index');
14
+ var tweets = $('#tweets').bindto('tweets');
16
15
 
17
- $('#updateSubmit').click(function(){
18
- tweets.invoke('update', $('#updateText').val());
19
- return false;
20
- });
16
+ $('#updateSubmit').click(function(){
17
+ tweets.invoke('update', $('#updateText').val());
18
+ return false;
21
19
  });
22
20
  });
23
21
  </script>
data/examples/users.rb CHANGED
@@ -1,12 +1,7 @@
1
1
  module Binders
2
- class Users < Bowline::Binders::Collection
3
- class << self
4
- # self.items is a special method
5
- # Basically it'll update users on the client side
6
- def index
7
- self.items = User.all
8
- end
9
-
2
+ class Users < Bowline::Binders::Base
3
+ bind User
4
+ class << self
10
5
  def admins
11
6
  self.items = User.admins.all
12
7
  end
@@ -14,23 +9,22 @@ module Binders
14
9
 
15
10
  def update(attrs)
16
11
  if @item.update_attributes(attrs)
17
- flash[:notice] = "Successfully updated"
12
+ page.flash("Successfully updated").call
18
13
  else
19
- flash[:notice] = "Errors updating users"
14
+ page.flash_error("Errors updating users").call
20
15
  end
21
16
  end
22
17
 
23
18
  def highlight
24
19
  # Calls $('user_1').highlight()
25
- self.element.highlight
20
+ self.element.highlight.call
26
21
  end
27
22
 
28
- # Overrides charge on user
29
23
  def charge!
30
- # calls charge! on model (i.e. do sql commit )
24
+ # calls charge! on model (i.e. do SQL commit )
31
25
  self.item.charge!
32
26
  # Now gui stuff
33
- flash[:notice] = "Successfully charged"
27
+ page.flash("Charged!")
34
28
  highlight
35
29
  end
36
30
  end
@@ -1,23 +1,87 @@
1
1
  module Bowline
2
2
  module Binders
3
+ # Binders are a central part of Bowline. They perform two main functions:
4
+ # 1) Bind a model to the view, so any changes to the model get automatically
5
+ # reflected in the view.
6
+ # 2) View abstraction of the model. You can define view specific class & instance
7
+ # methods, and easily call them from bound JavaScript objects.
8
+ #
9
+ # To use a binder, you first need to bind it to a model using the bind method.
10
+ # Example:
11
+ # class UsersBinder < Bowline::Binders::Base
12
+ # bind User
13
+ # end
14
+ #
15
+ # Once a class is bound, any updates to the model automatically update any bound HTML.
16
+ # The class names in the HTML are tied to the model's attribute names.
17
+ # You can bind HTML using the bowline.js bindup function.
18
+ # Example:
19
+ # <div id="users">
20
+ # <div class="item">
21
+ # <span class="name"></span>
22
+ # </div>
23
+ # </div>
24
+ # <script>
25
+ # $("#users").bindup('UsersBinder');
26
+ # </script>
27
+ #
28
+ # =Class methods
29
+ #
30
+ # You can define class methods on your binder, and call them using JavaScript
31
+ # using the invoke function on the bound HTML element.
32
+ # Example:
33
+ # <script>
34
+ # var users = $("#users").bindup('UsersBinder');
35
+ # users.invoke("method_name", "arg1", "arg2")
36
+ # </script>
37
+ #
38
+ # =Instance methods
39
+ #
40
+ # You can call your binders instance method from JavaScript by calling the invoke
41
+ # function on the generated HTML elements. Your binder's instance methods have access
42
+ # to an 'element' variable, which is the jQuery element, and a 'item' variable, which
43
+ # is the bound model's instance record.
44
+ #
45
+ # Example:
46
+ # class UsersBinder < Bowline::Binders::Base
47
+ # bind User
48
+ # def charge!
49
+ # #...
50
+ # end
51
+ # end
52
+ #
53
+ # <script>
54
+ # $('#users').items(10).invoke('charge!');
55
+ # </script>
56
+ #
57
+ # For more documentation on Bowline's JavaScript API, see bowline.js
3
58
  class Base
4
59
  extend Bowline::Watcher::Base
5
60
  extend Bowline::Desktop::Bridge::ClassMethods
6
61
  js_expose
7
62
 
8
63
  class << self
64
+ # An array of window currently bound.
9
65
  def windows
10
66
  @windows ||= []
11
67
  end
12
68
 
13
- # Called by JS when first bound
14
- def setup(window)
69
+ # Called by a window's JavaScript whenever that window is bound to this Binder.
70
+ # This method populates the window's HTML with all bound class' records.
71
+ # Override this if you don't want to send all the class' records to the window.
72
+ # Example:
73
+ # def setup(window)
74
+ # super(window, last_10_tweets)
75
+ # end
76
+ def setup(window, items = all)
15
77
  self.windows << window
16
- self.items = all
78
+ window.bowline.populate(
79
+ name, items.to_js
80
+ ).call
17
81
  true
18
82
  end
19
83
 
20
- def js_invoke(window, method, *args)
84
+ def js_invoke(window, method, *args) #:nodoc:
21
85
  if method == :setup
22
86
  setup(window)
23
87
  else
@@ -29,19 +93,28 @@ module Bowline
29
93
  self.new(id).send(meth, *args)
30
94
  end
31
95
 
96
+ # Calls .find on the klass sent to the bind method.
97
+ # This is used internally, to find records when the page
98
+ # invoke instance methods.
32
99
  def find(id)
33
100
  klass.find(id)
34
101
  end
35
-
102
+
103
+ # Calls .all on the klass sent to the bind method.
104
+ # This method is called internally by the setup method.
36
105
  def all
37
106
  klass.all
38
107
  end
39
108
 
40
- def items=(items) #:nodoc:
109
+ # Set the binder's items. This will replace all items, and update the HTML.
110
+ def items=(items)
41
111
  bowline.populate(name, items.to_js).call
42
112
  end
43
113
 
44
- def created(item) #:nodoc:
114
+ # Add a new item to the binder, updating the HTML.
115
+ # This method is normally only called internally by
116
+ # the bound class's after_create callback.
117
+ def created(item)
45
118
  bowline.created(
46
119
  name,
47
120
  item.id,
@@ -49,7 +122,10 @@ module Bowline
49
122
  ).call
50
123
  end
51
124
 
52
- def updated(item) #:nodoc:
125
+ # Update an item on the binder, updating the HTML.
126
+ # This method is normally only called internally by
127
+ # the bound class's after_update callback.
128
+ def updated(item)
53
129
  bowline.updated(
54
130
  name,
55
131
  item.id,
@@ -57,7 +133,10 @@ module Bowline
57
133
  ).call
58
134
  end
59
135
 
60
- def removed(item) #:nodoc:
136
+ # Remove an item from the binder, updating the HTML.
137
+ # This method is normally only called internally by
138
+ # the bound class's after_destroy callback.
139
+ def removed(item)
61
140
  bowline.removed(
62
141
  name,
63
142
  item.id
@@ -65,41 +144,45 @@ module Bowline
65
144
  end
66
145
 
67
146
  protected
68
- # Associate the binder with a model
69
- # to setup callbacks so changes to the
70
- # model are automatically reflected in
71
- # the view. Usage:
72
- # expose Post
147
+ # Associate the binder with a model to setup callbacks so
148
+ # changes to the model are automatically reflected in the view.
149
+ # Example:
150
+ # bind Post
73
151
  #
74
- # When the exposed class is created/updated/deleted
152
+ # When the bound class is created/updated/deleted
75
153
  # the binder's callbacks are executed and the view
76
154
  # updated accordingly.
77
- #
78
- # klass needs to respond to:
79
- # * all
80
- # * find(id)
81
- # * after_create(method)
82
- # * after_update(method)
83
- # * after_destroy(method)
155
+ #
156
+ # Classes inheriting fromActiveRecord and Bowline::LocalModel are
157
+ # automatically compatable, but if you're using your own custom model
158
+ # you need to make sure it responds to the following methods:
159
+ # * all - return all records
160
+ # * find(id) - find record by id
161
+ # * after_create(method) - after_create callback
162
+ # * after_update(method) - after_update callback
163
+ # * after_destroy(method) - after_destroy callback
84
164
  #
85
- # klass instance needs to respond to:
86
- # * id
165
+ # The klass' instance needs to respond to:
166
+ # * id - returns record id
167
+ # * to_js - return record's attribute hash
87
168
  #
88
- # You can override .to_js on the model instance
89
- # in order to return specific attributes for the view
90
- def expose(klass)
169
+ # You can override the to_js method on the model instance
170
+ # in order to return specific attributes for the view.
171
+ def bind(klass)
91
172
  @klass = klass
92
173
  @klass.after_create(method(:created))
93
174
  @klass.after_update(method(:updated))
94
175
  @klass.after_destroy(method(:removed))
95
176
  end
96
177
 
97
- # Returns class set by the 'expose' method
178
+ # Returns class set by the 'bind' method
98
179
  def klass
99
- @klass || raise("klass not set - see expose method")
180
+ @klass || raise("klass not set - see bind method")
100
181
  end
101
182
 
102
- # JavaScript proxy to the page:
183
+ # JavaScript proxy to the page.
184
+ # See Bowline::Desktop::Proxy for more information.
185
+ # Example:
103
186
  # page.myFunc(1,2,3).call
104
187
  def page
105
188
  Bowline::Desktop::Proxy.new(
@@ -107,14 +190,18 @@ module Bowline
107
190
  )
108
191
  end
109
192
 
110
- # JavaScript proxy to the Bowline object:
193
+ # JavaScript proxy to the Bowline object.
194
+ # See Bowline::Desktop::Proxy for more information.
195
+ # Example:
111
196
  # bowline.log("msg").call
112
197
  def bowline
113
198
  page.Bowline
114
199
  end
115
200
 
116
- # Javascript proxy to jQuery:
117
- # jquery.getJSON("http://example.com")
201
+ # Javascript proxy to jQuery.
202
+ # See Bowline::Desktop::Proxy for more information.
203
+ # Example:
204
+ # jquery.getJSON("http://example.com").call
118
205
  def jquery
119
206
  page.jQuery
120
207
  end
@@ -125,7 +212,8 @@ module Bowline
125
212
  end
126
213
 
127
214
  # Trigger events on all elements
128
- # bound to this binder:
215
+ # bound to this binder.
216
+ # Example:
129
217
  # trigger(:reload, {:key => :value})
130
218
  def trigger(event, data = nil)
131
219
  bowline.trigger(
@@ -152,14 +240,13 @@ module Bowline
152
240
  name.to_s
153
241
  end
154
242
  end
155
-
243
+
244
+ # jQuery element object
156
245
  attr_reader :element
157
- attr_reader :item
158
246
 
159
- # Instance of an element on the view.
160
- #
161
- # item.destroy
162
- # element.highlight.call
247
+ # Instance of the bound class' record
248
+ attr_reader :item
249
+
163
250
  def initialize(id, *args) #:nodoc:
164
251
  @element = self.class.bowline.element(
165
252
  self.class.name, id
@@ -168,7 +255,8 @@ module Bowline
168
255
  end
169
256
 
170
257
  protected
171
- # Trigger jQuery events on this element:
258
+ # Trigger jQuery events on this element.
259
+ # Example:
172
260
  # trigger(:highlight)
173
261
  def trigger(event, data = nil)
174
262
  element.trigger(
@@ -184,7 +272,7 @@ module Bowline
184
272
 
185
273
  # Shortcut methods
186
274
 
187
- # See self.class.js
275
+ # See self.class.page
188
276
  def page
189
277
  self.class.page
190
278
  end
@@ -1,4 +1,5 @@
1
- module Dependencies; end
1
+ module Dependencies #:nodoc: all
2
+ end
2
3
 
3
4
  Dir[File.join(File.dirname(__FILE__), 'dependencies', '*.rb')].each do |file|
4
5
  require file
@@ -22,7 +22,7 @@ Gem.pre_install_hooks.push(proc do |installer|
22
22
  end
23
23
  end)
24
24
 
25
- class ::Gem::Uninstaller
25
+ class ::Gem::Uninstaller #:nodoc:
26
26
  def self._with_silent_ui
27
27
 
28
28
  ui = Gem::DefaultUserInteraction.ui
@@ -64,7 +64,7 @@ Gem.post_install_hooks.push(proc do |installer|
64
64
  )
65
65
  end)
66
66
 
67
- class ::Gem::DependencyInstaller
67
+ class ::Gem::DependencyInstaller #:nodoc:
68
68
  alias old_fg find_gems_with_sources
69
69
 
70
70
  def find_gems_with_sources(dep)
@@ -78,7 +78,7 @@ class ::Gem::DependencyInstaller
78
78
  end
79
79
  end
80
80
 
81
- class ::Gem::SpecFetcher
81
+ class ::Gem::SpecFetcher #:nodoc:
82
82
  alias old_fetch fetch
83
83
  def fetch(*args) # in rubygems 1.3.2 fetch takes 4 parameters
84
84
  dependency, all, matching_platform, prerelease = *args
@@ -102,7 +102,7 @@ class ::Gem::SpecFetcher
102
102
  end
103
103
  end
104
104
 
105
- class ::Gem::Specification
105
+ class ::Gem::Specification #:nodoc:
106
106
  def recursive_dependencies(from, index = Gem.source_index)
107
107
  specs = self.runtime_dependencies.map do |dep|
108
108
  spec = index.search(dep).last
@@ -4,19 +4,28 @@ module Bowline
4
4
  extend Bowline::Watcher::Base
5
5
  watch :on_tick, :on_idle
6
6
 
7
+ ##
8
+ # :singleton-method: on_tick(method = nil, &block)
9
+ # A Watcher event method that gets called every few microseconds,
10
+ # inside the application's main thread.
11
+ # Example:
12
+ # on_tick { puts "App tick" }
13
+
14
+ # Return true if we're currently
15
+ # being executed by bowline-desktop.
7
16
  def enabled?
8
17
  $0 == "bowline"
9
18
  end
10
19
  module_function :enabled?
11
20
 
12
- def idle
21
+ def idle #:nodoc:
13
22
  watcher.call(:on_idle)
14
23
  rescue => e
15
24
  log_error e
16
25
  end
17
26
  module_function :idle
18
27
 
19
- def tick
28
+ def tick #:nodoc:
20
29
  watcher.call(:on_tick)
21
30
  rescue => e
22
31
  log_error e