bowline 0.6.2 → 0.6.3

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.
data/README.txt CHANGED
@@ -12,7 +12,7 @@ Ruby, HTML and JS desktop application framework.
12
12
  * Uses Webkit
13
13
  * View in HTML/JavaScript
14
14
  * Binding between HTML & Ruby
15
- * Cross platform (only osx atm ;)
15
+ * Cross platform (OSX & Ubuntu) - Windows soon
16
16
 
17
17
  = INTRODUCTION
18
18
 
@@ -114,12 +114,7 @@ Suffice to say, the HTML looks a bit like this:
114
114
  <a href="#" class="destroy">Delete</a>
115
115
  </div>
116
116
  </div>
117
-
118
- Now, were you to have a user object, you could do something like
119
- this to update the HTML.
120
-
121
- # TODO
122
-
117
+
123
118
  = METHODS IN BINDERS
124
119
 
125
120
  You can call both class and instance methods of the binders.
@@ -189,7 +184,6 @@ Usage for a collection (of users):
189
184
  def admins
190
185
  # This just replaces all the listed
191
186
  # users with just admins
192
- # TODO
193
187
  self.items = User.admins.all
194
188
  end
195
189
  end
data/TODO CHANGED
@@ -1,5 +1,3 @@
1
- Use new Rails gem packaging system
2
-
3
1
  Ideas:
4
2
  * bowline-notification gem (Growl)
5
3
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.2
1
+ 0.6.3
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{bowline}
8
- s.version = "0.6.2"
8
+ s.version = "0.6.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Alex MacCaw"]
12
- s.date = %q{2010-02-25}
12
+ s.date = %q{2010-04-06}
13
13
  s.default_executable = %q{bowline-gen}
14
14
  s.description = %q{Ruby/JS GUI framework}
15
15
  s.email = %q{alex@leadthinking.com}
@@ -55,6 +55,7 @@ Gem::Specification.new do |s|
55
55
  "lib/bowline/commands/build.rb",
56
56
  "lib/bowline/commands/console.rb",
57
57
  "lib/bowline/commands/generate.rb",
58
+ "lib/bowline/commands/init.rb",
58
59
  "lib/bowline/commands/run.rb",
59
60
  "lib/bowline/desktop.rb",
60
61
  "lib/bowline/desktop/app.rb",
@@ -102,6 +103,8 @@ Gem::Specification.new do |s|
102
103
  "templates/config/boot.rb",
103
104
  "templates/config/database.yml",
104
105
  "templates/config/environment.rb",
106
+ "templates/config/environments/development.rb",
107
+ "templates/config/environments/production.rb",
105
108
  "templates/gitignore",
106
109
  "templates/helper.rb",
107
110
  "templates/main_window.rb",
@@ -7,24 +7,19 @@ require 'active_support/core_ext/hash/indifferent_access'
7
7
  Thread.abort_on_exception = true
8
8
 
9
9
  module Bowline
10
- def lib_path
10
+ def library_path
11
11
  File.expand_path(
12
- File.join(File.dirname(__FILE__), *%w[..])
12
+ File.join(File.dirname(__FILE__), "..")
13
13
  )
14
14
  end
15
- module_function :lib_path
16
-
17
- def assets_path
18
- File.join(lib_path, "assets")
19
- end
20
- module_function :assets_path
15
+ module_function :library_path
21
16
 
22
17
  module Base
23
18
  end
24
19
  end
25
20
 
26
21
  $LOAD_PATH.unshift(File.dirname(__FILE__))
27
- $LOAD_PATH << File.join(Bowline.lib_path, 'vendor')
22
+ $LOAD_PATH << File.join(Bowline.library_path, 'vendor')
28
23
 
29
24
  require 'bowline/version'
30
25
 
@@ -46,9 +41,11 @@ require 'bowline/desktop/dock'
46
41
  require 'bowline/desktop/host'
47
42
  require 'bowline/desktop/misc'
48
43
  require 'bowline/desktop/network'
44
+ require 'bowline/desktop/path'
49
45
  require 'bowline/desktop/sound'
50
46
  require 'bowline/desktop/window_methods'
51
47
  require 'bowline/desktop/window'
48
+ require 'bowline/desktop/runtime'
52
49
  require 'bowline/desktop/js'
53
50
  require 'bowline/desktop/proxy'
54
51
  require 'bowline/desktop/bridge'
@@ -1,41 +1,28 @@
1
+ require 'supermodel'
2
+
1
3
  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
4
+ class AppConfig < SuperModel::Base
5
+ include SuperModel::Marshal::Model
23
6
 
24
- def method_missing(sym, *args)
25
- method_name = sym.to_s
7
+ class << self
8
+ def instance
9
+ @instance ||= create
10
+ end
11
+
12
+ def marshal_records(record = nil)
13
+ self.instance.load(record.attributes) if record
14
+ self.instance
15
+ end
26
16
 
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
17
+ def load!(path)
18
+ self.instance.load_path(path)
19
+ self.instance
38
20
  end
39
21
  end
22
+
23
+ def load_path(path)
24
+ return unless path && File.exist?(path)
25
+ load(YAML::load(File.read(path)))
26
+ end
40
27
  end
41
28
  end
@@ -60,7 +60,60 @@ module Bowline
60
60
  extend Bowline::Desktop::Bridge::ClassMethods
61
61
  js_expose
62
62
 
63
- class << self
63
+ module Async
64
+ protected
65
+ def async(*methods)
66
+ if block_given?
67
+ Thread.new(callback_proc) do |proc|
68
+ begin
69
+ self.callback_proc = proc
70
+ yield
71
+ rescue => e
72
+ Bowline::Logging.log_error(e)
73
+ end
74
+ end
75
+ else
76
+ methods.each do |method|
77
+ class_eval(<<-EOS, __FILE__, __LINE__)
78
+ def #{method}_with_async(*args, &block)
79
+ async { #{method}_without_async(*args, &block) }
80
+ end
81
+ EOS
82
+ alias_method_chain method, :async
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ extend Async
89
+ include Async
90
+ class << self; extend Async; end
91
+
92
+ class << self
93
+ def callback_proc(proc = nil) #:nodoc:
94
+ Thread.current[:callback] = proc if proc
95
+ Thread.current[:callback]
96
+ end
97
+ alias_method :callback_proc=, :callback_proc
98
+
99
+ def callback(result = nil)
100
+ result = yield if block_given?
101
+ callback_proc.call(result)
102
+ end
103
+
104
+ def js_invoke(window, callback, method, *args) #:nodoc:
105
+ self.callback_proc = callback
106
+ if method == :setup
107
+ setup(window)
108
+ else
109
+ send(method, *args)
110
+ end
111
+ end
112
+
113
+ def instance_invoke(id, method, *args) #:nodoc:
114
+ self.new(id).send(method, *args)
115
+ end
116
+
64
117
  # An array of window currently bound.
65
118
  def windows
66
119
  @windows ||= []
@@ -68,10 +121,11 @@ module Bowline
68
121
 
69
122
  def setup(window) #:nodoc:
70
123
  self.windows << window
124
+ self.windows.uniq!
71
125
  if initial_items = initial
72
126
  self.items = initial_items
73
127
  end
74
- Hash.new # Empty options
128
+ callback(true)
75
129
  end
76
130
 
77
131
  # Called by a window's JavaScript whenever that window is bound to this Binder.
@@ -84,18 +138,6 @@ module Bowline
84
138
  def initial
85
139
  end
86
140
 
87
- def js_invoke(window, method, *args) #:nodoc:
88
- if method == :setup
89
- setup(window)
90
- else
91
- send(method, *args)
92
- end
93
- end
94
-
95
- def instance_invoke(id, meth, *args) #:nodoc:
96
- self.new(id).send(meth, *args)
97
- end
98
-
99
141
  # Calls .find on the klass sent to the bind method.
100
142
  # This is used internally, to find records when the page
101
143
  # invoke instance methods.
@@ -133,8 +175,8 @@ module Bowline
133
175
  # Remove an item from the binder, updating the HTML.
134
176
  # This method is normally only called internally by
135
177
  # the bound class's after_destroy callback.
136
- def removed(item)
137
- bowline.removed(
178
+ def destroyed(item)
179
+ bowline.destroyed(
138
180
  name,
139
181
  item.id
140
182
  ).call
@@ -174,62 +216,26 @@ module Bowline
174
216
  # See Bowline::logger
175
217
  def logger
176
218
  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
219
+ end
207
220
  end
208
-
209
- # jQuery element object
210
- attr_reader :element
211
-
221
+
212
222
  # Instance of the bound class' record
213
223
  attr_reader :item
214
224
 
215
225
  def initialize(id, *args) #:nodoc:
216
- @element = self.class.bowline.element(
217
- self.class.name, id
218
- )
219
- @item = self.class.find(id)
226
+ @item = self.class.find(id)
220
227
  end
221
228
 
222
229
  protected
223
- # Trigger jQuery events on this element.
224
- # Example:
225
- # trigger(:highlight)
226
- def trigger(event, data = nil)
227
- element.trigger(
228
- self.class.format_event(event),
229
- data
230
- ).call
230
+ def callback_proc(*args) #:nodoc
231
+ self.class.callback_proc(*args)
231
232
  end
232
-
233
+ alias_method :callback_proc=, :callback_proc
234
+
235
+ def callback(*args, &block)
236
+ self.class.callback(*args, &block)
237
+ end
238
+
233
239
  # Remove element from the view
234
240
  def remove!
235
241
  self.class.removed(item)
@@ -15,7 +15,7 @@ module Bowline
15
15
  end
16
16
 
17
17
  def after_destroy(rec)
18
- binder.removed(rec)
18
+ binder.destroyed(rec)
19
19
  end
20
20
 
21
21
  def update(observed_method, object) #:nodoc:
@@ -2,11 +2,6 @@ module Bowline
2
2
  module Binders
3
3
  class Singleton < Base
4
4
  class << self
5
- def setup(*args) #:nodoc
6
- super(*args)
7
- {:singleton => true}
8
- end
9
-
10
5
  alias :item= :items=
11
6
 
12
7
  # Associate the binder with a model to setup callbacks so
@@ -1,23 +1,2 @@
1
- require "optparse"
2
- require "irb"
3
- require "irb/completion"
4
-
5
- options = {}
6
- OptionParser.new do |opt|
7
- opt.banner = "Usage: console [environment] [options]"
8
- opt.on("--debugger", "Enable ruby-debugging for the console.") { |v| options[:debugger] = v }
9
- opt.parse!(ARGV)
10
- end
11
-
12
- if options[:debugger]
13
- begin
14
- require "ruby-debug"
15
- puts "=> Debugger enabled"
16
- rescue Exception
17
- puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
18
- exit
19
- end
20
- end
21
-
22
- require "#{APP_ROOT}/config/environment"
23
- IRB.start
1
+ ENV["APP_IRB"] = "1"
2
+ require "bowline/commands/run"
@@ -1,3 +1,3 @@
1
- require 'bowline/generators'
1
+ require "bowline/generators"
2
2
 
3
- Bowline::Generators.run_cli(APP_ROOT, 'bowline', Bowline::Version::STRING, ARGV)
3
+ Bowline::Generators.run_cli(APP_ROOT, "bowline", Bowline::Version::STRING, ARGV)
@@ -0,0 +1,12 @@
1
+ unless Bowline::Desktop.enabled?
2
+ raise "Script not executed by bowline-desktop"
3
+ end
4
+
5
+ require Bowline.root.join(*%w{config environment})
6
+
7
+ if ENV["APP_IRB"]
8
+ require "irb"
9
+ require "irb/completion"
10
+ IRB.start
11
+ Bowline::Desktop::App.exit
12
+ end
@@ -4,4 +4,4 @@ unless Bowline::Library.ready?
4
4
  Rake::Task['libs:setup'].invoke
5
5
  end
6
6
 
7
- exec("#{Bowline::Library.desktop_path} #{APP_ROOT}")
7
+ exec("#{Bowline::Library.desktop_path} #{Bowline.root}")
@@ -30,6 +30,6 @@ module Bowline
30
30
  rescue => e
31
31
  log_error e
32
32
  end
33
- module_function :tick
33
+ module_function :tick
34
34
  end
35
35
  end
@@ -30,8 +30,8 @@ module Bowline
30
30
  true
31
31
  end
32
32
 
33
- def js_invoke(window, method, *args)
34
- send(method, *args)
33
+ def js_invoke(window, callback, method, *args)
34
+ callback.call(send(method, *args))
35
35
  end
36
36
  RUBY
37
37
  end
@@ -40,20 +40,31 @@ module Bowline
40
40
  class Message #:nodoc:
41
41
  include Bowline::Logging
42
42
 
43
- attr_reader :window, :id, :klass, :method, :args
43
+ attr_reader :window, :id, :klass, :method_name, :args
44
44
 
45
45
  def initialize(window, atts)
46
- @window = window
47
- atts = atts.with_indifferent_access
48
- @id = atts[:id]
49
- @klass = atts[:klass]
50
- @method = atts[:method].to_sym
51
- @args = atts[:args] || []
46
+ @window = window
47
+ atts = atts.with_indifferent_access
48
+ @id = atts[:id]
49
+ @klass = atts[:klass]
50
+ @method_name = atts[:method].to_sym
51
+ @args = atts[:args] || []
52
52
  end
53
53
 
54
54
  def callback?
55
55
  @id != -1
56
56
  end
57
+
58
+ def callback(result)
59
+ return unless callback?
60
+ proxy = Proxy.new(window)
61
+ proxy = proxy.Bowline.invokeCallback(
62
+ id, result.to_js.to_json
63
+ )
64
+ Runtime.main {
65
+ window.run_script(proxy.to_s)
66
+ }
67
+ end
57
68
 
58
69
  def invoke
59
70
  if klass == "_window"
@@ -62,14 +73,19 @@ module Bowline
62
73
  # TODO - security concerns with constantize
63
74
  object = klass.constantize
64
75
  end
65
- trace "JS invoking: #{klass}.#{method}(#{args.join(',')})"
66
- if object.respond_to?(:js_exposed?) && object.js_exposed?(method)
67
- result = object.js_invoke(window, method, *args)
68
- if callback?
69
- proxy = Proxy.new(window)
70
- proxy.Bowline.invokeCallback(id, result.to_js.to_json)
71
- window.run_script(proxy.to_s)
72
- end
76
+
77
+ trace "JS invoking: #{klass}.#{method_name}(#{args.join(',')})"
78
+
79
+ if object.respond_to?(:js_exposed?) &&
80
+ object.js_exposed?(method_name)
81
+
82
+ object.js_invoke(
83
+ window,
84
+ method(:callback),
85
+ method_name,
86
+ *args
87
+ )
88
+
73
89
  else
74
90
  raise "Method not allowed"
75
91
  end
@@ -82,7 +98,8 @@ module Bowline
82
98
  result = JSON.parse(str)
83
99
  Message.new(window, result).invoke
84
100
  rescue => e
85
- Bowline::Logging.log_error e
101
+ Bowline::Logging.trace(str)
102
+ Bowline::Logging.log_error(e)
86
103
  end
87
104
  module_function :call
88
105
  end
@@ -55,20 +55,15 @@ module Bowline
55
55
  raise e
56
56
  end
57
57
  end
58
-
59
- def poll
60
- run_scripts
61
- end
62
- module_function :poll
63
58
 
64
- def setup
59
+ def setup! #:nodoc:
65
60
  Desktop.on_idle(method(:poll))
66
61
  end
67
- module_function :setup
62
+ module_function :setup!
68
63
 
69
- def eval(win, str, method = nil, &block)
70
- script = Script.new(win, str, method||block)
71
- if Thread.current == Thread.main && script.ready?
64
+ def eval(window, string, method = nil, &block)
65
+ script = Script.new(window, string, method||block)
66
+ if Runtime.main_thread? && script.ready?
72
67
  script.call
73
68
  else
74
69
  scripts << script
@@ -77,18 +72,17 @@ module Bowline
77
72
  module_function :eval
78
73
 
79
74
  private
80
- def run_scripts
75
+ def poll
81
76
  ready_scripts = scripts.select(&:ready?)
82
77
  ready_scripts.each do |script|
83
78
  script.call
84
79
  scripts.delete(script)
85
80
  end
86
81
  end
87
- module_function :run_scripts
82
+ module_function :poll
88
83
 
89
- # TODO - thread safety, needs mutex
90
84
  def scripts
91
- Thread.main[:scripts] ||= []
85
+ @scripts ||= []
92
86
  end
93
87
  module_function :scripts
94
88
  end
@@ -15,7 +15,7 @@ module Bowline
15
15
  end
16
16
 
17
17
  def offline!
18
- return unless @online
18
+ return if @online == false
19
19
  @online = false
20
20
  watcher.call(:on_offline)
21
21
  watcher.call(:on_change)
@@ -8,6 +8,12 @@ module Bowline
8
8
  # * Windows: "C:\Documents and Settings\username\My Documents"
9
9
  # * Mac: ~/Documents
10
10
 
11
+ # Get the users home dir
12
+ def home
13
+ Gem.user_home
14
+ end
15
+ module_function :home
16
+
11
17
  ##
12
18
  # :singleton-method: data
13
19
  # Get the app's data dir.
@@ -16,11 +22,22 @@ module Bowline
16
22
  # * Mac: appinfo.app/Contents/SharedSupport bundle subdirectory
17
23
 
18
24
  ##
19
- # :singleton-method: user_data
20
25
  # Get the app's user data dir.
21
26
  # * Unix: ~/.appinfo
22
27
  # * Windows: "C:\Documents and Settings\username\Application Data\appinfo"
23
28
  # * Mac: "~/Library/Application Support/appinfo".
29
+ def user_data
30
+ conf = Bowline.configuration
31
+ case Bowline::Platform.type
32
+ when :linux
33
+ File.join(home, "." + conf.name)
34
+ when :win32
35
+ File.join(home, "Application Data", conf.name)
36
+ when :osx
37
+ File.join(home, "Library", "Application Support", conf.name)
38
+ end
39
+ end
40
+ module_function :user_data
24
41
 
25
42
  ##
26
43
  # :singleton-method: temp
@@ -1,17 +1,30 @@
1
+ require "thread"
2
+
1
3
  module Bowline
2
4
  module Desktop
3
5
  module Runtime
4
- def setup
6
+ def setup! #:nodoc:
5
7
  Desktop.on_tick(method(:poll))
6
8
  end
7
- module_function :setup
9
+ module_function :setup!
8
10
 
9
- def run_in_main_thread(method = nil, &block)
10
- procs << method||block
11
+ # Run block/method in main thread
12
+ def main(method = nil, &block)
13
+ proc = method||block
14
+ if main_thread?
15
+ proc.call
16
+ else
17
+ procs << proc
18
+ end
19
+ end
20
+ module_function :main
21
+
22
+ def main_thread?
23
+ Thread.current == Thread.main
11
24
  end
12
- module_function :run_in_main_thread
25
+ module_function :main_thread?
13
26
 
14
- private
27
+ private
15
28
  def poll
16
29
  while proc = procs.shift
17
30
  proc.call
@@ -19,9 +32,8 @@ module Bowline
19
32
  end
20
33
  module_function :poll
21
34
 
22
- # TODO - thread safety, needs mutex
23
35
  def procs
24
- Thread.main[:procs] ||= []
36
+ @procs ||= []
25
37
  end
26
38
  module_function :procs
27
39
  end
@@ -4,7 +4,7 @@ module Bowline
4
4
  # It's the usual way of interfacing with your application's windows,
5
5
  # and all of Bowline's windows normally inherit from it.
6
6
  #
7
- # You'll need to call the setup method, before calling using this class.
7
+ # You'll need to call the setup! method, before calling using this class.
8
8
  # If the window is deallocated (i.e. closed), you'll need to call it again.
9
9
  # It's worth not calling the setup method before you need it, since it'll
10
10
  # increase the amount of CPU your application uses.
@@ -55,7 +55,7 @@ module Bowline
55
55
  # Call this method to allocate a new window.
56
56
  # You'll need to do this before using it,
57
57
  # or after it has been closed.
58
- def setup
58
+ def setup!
59
59
  return unless Desktop.enabled?
60
60
  return if @window && !@window.dealocated?
61
61
  if self.name == "MainWindow"
@@ -68,7 +68,7 @@ module Bowline
68
68
  @script_callback = Proc.new {|str|
69
69
  Bowline::Desktop::Bridge.call(self, str)
70
70
  }
71
- @window.script_callback = @script_callback;
71
+ @window.script_callback = @script_callback
72
72
  end
73
73
 
74
74
  # Evaluate JavaScript in this window. Pass
@@ -1,5 +1,5 @@
1
1
  class Array
2
- def to_js
3
- map {|i| i.to_js }
2
+ def to_js(*args)
3
+ map {|i| i.to_js(*args) }
4
4
  end
5
5
  end
@@ -2,9 +2,9 @@ class Object
2
2
  # Aim is to convert the object into:
3
3
  # * A hash or
4
4
  # * An array of hashes
5
- def to_js
6
- if respond_to?(:attribute_hash)
7
- attribute_hash
5
+ def to_js(*args)
6
+ if respond_to?(:serializable_hash)
7
+ serializable_hash(*args)
8
8
  elsif respond_to?(:attributes)
9
9
  attributes
10
10
  else
@@ -2,6 +2,43 @@ gem "templater", ">= 0.3.2"
2
2
  require "templater"
3
3
  require "rbconfig"
4
4
 
5
+ module Templater
6
+ module Actions
7
+ class Touch < Action
8
+
9
+ def initialize(generator, name, destination, options={})
10
+ self.generator = generator
11
+ self.name = name
12
+ self.destination = destination
13
+ self.options = options
14
+ end
15
+
16
+ def render
17
+ ''
18
+ end
19
+
20
+ def exists?
21
+ ::File.exists?(destination)
22
+ end
23
+
24
+ def identical?
25
+ exists?
26
+ end
27
+
28
+ def invoke!
29
+ callback(:before)
30
+ ::FileUtils.touch(destination)
31
+ callback(:after)
32
+ end
33
+
34
+ def revoke!
35
+ ::FileUtils.rm_rf(::File.expand_path(destination))
36
+ end
37
+
38
+ end # Touch
39
+ end # Actions
40
+ end # Templater
41
+
5
42
  module Bowline
6
43
  module Generators #:nodoc: all
7
44
  extend Templater::Manifold
@@ -27,6 +64,12 @@ module Bowline
27
64
  "#!/usr/bin/env #{RbConfig::CONFIG["RUBY_INSTALL_NAME"]}"
28
65
  end
29
66
 
67
+ def self.touch(name, destination)
68
+ empty_directories << Templater::ActionDescription.new(name) do |generator|
69
+ Templater::Actions::Touch.new(generator, name, destination)
70
+ end
71
+ end
72
+
30
73
  def self.source_root
31
74
  File.join(File.dirname(__FILE__), *%w[.. .. templates])
32
75
  end
@@ -5,11 +5,11 @@ module Bowline::Generators
5
5
  DESC
6
6
 
7
7
  def app_id
8
- ['bowline', name].join('.')
8
+ ["bowline", name].join(".")
9
9
  end
10
10
 
11
11
  def destination_root
12
- # Todo - only works relative
12
+ # TODO - only works relative
13
13
  File.join(@destination_root, base_name)
14
14
  end
15
15
 
@@ -19,7 +19,6 @@ module Bowline::Generators
19
19
 
20
20
  first_argument :name, :required => true, :desc => "application name"
21
21
 
22
- empty_directory :tmp, "tmp"
23
22
  empty_directory :vendor, "vendor"
24
23
  empty_directory :lib, "lib"
25
24
  empty_directory :db, "db"
@@ -60,7 +59,12 @@ module Bowline::Generators
60
59
  file(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
61
60
  }
62
61
 
62
+ glob! "config/environments"
63
+
63
64
  empty_directory :initializers, "config/initializers"
65
+ empty_directory :first_run, "config/first_run"
66
+
67
+ touch "app_first_run"
64
68
 
65
69
  file :readme, "../README.txt", "README"
66
70
  end
@@ -34,6 +34,22 @@ module Bowline
34
34
  def root
35
35
  Pathname.new(APP_ROOT) if defined?(APP_ROOT)
36
36
  end
37
+
38
+ def public_path
39
+ root && root.join("public")
40
+ end
41
+
42
+ def assets_path
43
+ File.join(library_path, "assets")
44
+ end
45
+
46
+ def env
47
+ @env ||= ActiveSupport::StringInquirer.new(ENV["APP_ENV"] || "development")
48
+ end
49
+
50
+ def irb?
51
+ !!ENV["APP_IRB"]
52
+ end
37
53
  end
38
54
 
39
55
  class Initializer #:nodoc:
@@ -61,7 +77,14 @@ module Bowline
61
77
  @configuration = configuration
62
78
  @loaded_plugins = []
63
79
  end
64
-
80
+
81
+ def set_environment
82
+ production_path = Bowline.root.join("app_production")
83
+ ENV["APP_ENV"] ||= begin
84
+ production_path.exist? ? "production" : "development"
85
+ end
86
+ end
87
+
65
88
  def require_frameworks
66
89
  configuration.frameworks.each { |framework| require(framework.to_s) }
67
90
  end
@@ -158,7 +181,7 @@ module Bowline
158
181
  # Loads support for "whiny nil" (noisy warnings when methods are invoked
159
182
  # on +nil+ values) if Configuration#whiny_nils is true.
160
183
  def initialize_whiny_nils
161
- require('active_support/whiny_nil') if configuration.whiny_nils
184
+ require("active_support/whiny_nil") if configuration.whiny_nils
162
185
  end
163
186
 
164
187
  # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
@@ -197,18 +220,32 @@ module Bowline
197
220
  end
198
221
 
199
222
  def load_plugins
200
- Dir.glob(File.join(configuration.plugin_glob, 'init.rb')).sort.each do |path|
223
+ Dir.glob(File.join(configuration.plugin_glob, "init.rb")).sort.each do |path|
201
224
  config = configuration # Need local config variable
202
225
  eval(IO.read(path), binding, path)
203
226
  end
204
227
  end
205
228
 
229
+ def load_application_environment
230
+ require(Bowline.root.join(*%w{config environments}, Bowline.env))
231
+ end
232
+
206
233
  def load_application_initializers
207
234
  Dir.glob(configuration.initializer_glob).sort.each do |initializer|
208
235
  load(initializer)
209
236
  end
210
237
  end
211
238
 
239
+ def load_application_first_run
240
+ first_run = Bowline.root.join("app_first_run")
241
+ if first_run.exist?
242
+ first_run.unlink
243
+ Dir.glob(configuration.first_run_glob).sort.each do |initializer|
244
+ load(initializer)
245
+ end
246
+ end
247
+ end
248
+
212
249
  def after_initialize
213
250
  configuration.after_initialize_blocks.each do |block|
214
251
  block.call
@@ -253,31 +290,39 @@ module Bowline
253
290
  # Creates a class called AppConfig from configuration
254
291
  # variables found in config/application.yml
255
292
  def load_app_config
256
- Object.const_set("AppConfig", AppConfig.new(configuration.app_config_file))
293
+ Object.const_set("AppConfig", AppConfig.load!(configuration.app_config_file))
257
294
  end
258
295
 
259
- def initialize_js
260
- return unless Bowline::Desktop.enabled?
261
- Bowline::Desktop::JS.setup
296
+ def initialize_desktop
297
+ return unless Desktop.enabled?
298
+ Desktop::Runtime.setup!
299
+ Desktop::JS.setup!
262
300
  end
263
301
 
264
302
  def initialize_windows
265
- return unless Bowline::Desktop.enabled?
266
- MainWindow.setup
303
+ return unless Desktop.enabled?
304
+ MainWindow.setup!
267
305
  MainWindow.title = configuration.name
268
306
  end
269
307
 
270
308
  def initialize_trap
271
- return unless Bowline::Desktop.enabled?
309
+ return unless Desktop.enabled?
272
310
  trap("INT") {
273
- Bowline::Desktop::App.exit
311
+ Desktop::App.exit
274
312
  }
275
313
  end
276
314
 
315
+ def initialize_path
316
+ # Dir::tmpdir/Tempfile uses this
317
+ ENV["TMPDIR"] = Desktop::Path.temp if Desktop.enabled?
318
+ FileUtils.mkdir_p(Desktop::Path.user_data)
319
+ end
320
+
277
321
  def initialize_marshal
278
322
  return unless defined?(SuperModel)
279
- FileUtils.mkdir_p(File.dirname(configuration.marshal_path))
280
- SuperModel::Marshal.path = configuration.marshal_path
323
+ path = configuration.marshal_path
324
+ path = path.call if path.is_a?(Proc)
325
+ SuperModel::Marshal.path = path
281
326
  SuperModel::Marshal.load
282
327
  at_exit {
283
328
  SuperModel::Marshal.dump
@@ -287,6 +332,8 @@ module Bowline
287
332
  def process
288
333
  Bowline.configuration = configuration
289
334
 
335
+ set_environment
336
+
290
337
  set_load_path
291
338
  load_gems
292
339
 
@@ -314,16 +361,18 @@ module Bowline
314
361
  load_plugins
315
362
  load_application_classes
316
363
  load_application_helpers
317
-
318
- load_application_initializers
319
364
 
320
- after_initialize
321
-
322
- initialize_js
365
+ initialize_desktop
323
366
  initialize_windows
324
367
  initialize_trap
325
-
368
+ initialize_path
326
369
  initialize_marshal
370
+
371
+ load_application_environment
372
+ load_application_initializers
373
+ load_application_first_run
374
+
375
+ after_initialize
327
376
 
328
377
  Bowline.initialized = true
329
378
  end
@@ -419,9 +468,8 @@ module Bowline
419
468
  attr_accessor :time_zone
420
469
 
421
470
  attr_accessor :plugin_glob
422
-
423
471
  attr_accessor :helper_glob
424
-
472
+ attr_accessor :first_run_glob
425
473
  attr_accessor :initializer_glob
426
474
 
427
475
  # Set the application's name.
@@ -473,6 +521,7 @@ module Bowline
473
521
  self.plugin_glob = default_plugin_glob
474
522
  self.helper_glob = default_helper_glob
475
523
  self.initializer_glob = default_initalizer_glob
524
+ self.first_run_glob = default_first_run_glob
476
525
  self.publisher = default_publisher
477
526
  self.copyright = default_copyright
478
527
 
@@ -589,7 +638,7 @@ module Bowline
589
638
  end
590
639
 
591
640
  def default_marshal_path
592
- File.join(root_path, 'db', 'marshal.db')
641
+ Proc.new { File.join(Desktop::Path.user_data, 'marshal.db') }
593
642
  end
594
643
 
595
644
  def default_cache_classes
@@ -611,6 +660,10 @@ module Bowline
611
660
  def default_initalizer_glob
612
661
  File.join(root_path, *%w{ config initializers **/*.rb })
613
662
  end
663
+
664
+ def default_first_run_glob
665
+ File.join(root_path, *%w{ config first_run **/*.rb })
666
+ end
614
667
 
615
668
  def default_publisher
616
669
  "Bowline"
@@ -1,3 +1,5 @@
1
+ require "pathname"
2
+
1
3
  module Bowline
2
4
  # Provides paths to Bowline's required libraries.
3
5
  module Library
@@ -8,50 +10,32 @@ module Bowline
8
10
  # Path to a folder stored under the users
9
11
  # home directory containing the downloaded libraries.
10
12
  def path
11
- File.expand_path(
12
- File.join(home_path, ".bowline")
13
+ Pathname.new(
14
+ File.expand_path(
15
+ File.join(Gem.user_home, ".bowline")
16
+ )
13
17
  )
14
18
  end
15
- module_function :path
16
19
 
17
20
  # Path to the bowline-desktop binary
18
21
  def desktop_path
19
- File.join(path, "bowline-desktop")
22
+ path.join("bowline-desktop")
20
23
  end
21
- module_function :desktop_path
22
24
 
23
25
  def libs_path
24
- File.join(path, "libs")
26
+ path.join("libs")
25
27
  end
26
- module_function :libs_path
27
28
 
28
29
  def local_build_path
29
- File.join(APP_ROOT, "build")
30
+ Bowline.root.join("build")
30
31
  end
31
- module_function :local_build_path
32
32
 
33
33
  # Returns true if all required libraries exist.
34
34
  def ready?
35
35
  File.exist?(desktop_path) &&
36
36
  File.directory?(libs_path)
37
37
  end
38
- module_function :ready?
39
-
40
- private
41
- # Borrowed from Rubygems
42
- def home_path
43
- ['HOME', 'USERPROFILE'].each do |homekey|
44
- return ENV[homekey] if ENV[homekey]
45
- end
46
- if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
47
- return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
48
- end
49
- begin
50
- File.expand_path("~")
51
- rescue
52
- File::ALT_SEPARATOR ? "C:/" : "/"
53
- end
54
- end
55
- module_function :home_path
38
+
39
+ extend self
56
40
  end
57
41
  end
@@ -1,9 +1,43 @@
1
1
  require 'fileutils'
2
2
  require 'erb'
3
3
  require 'rbconfig'
4
+ require 'tempfile'
5
+ require 'zip/zip'
4
6
 
5
7
  namespace :app do
6
- namespace :build do
8
+ namespace :build do
9
+ task :zip => :environment do
10
+ unless Bowline::Library.ready?
11
+ Rake::Task["libs:setup"].invoke
12
+ end
13
+
14
+ config = Bowline.configuration
15
+
16
+ build_path = Bowline::Library.local_build_path
17
+ FileUtils.mkdir_p(build_path)
18
+
19
+ app_path = File.join(build_path, "#{config.name}.zip")
20
+ FileUtils.rm_rf(app_path)
21
+
22
+ dirs = Dir[Bowline.root.join("**/**").to_s]
23
+ dirs.delete(build_path)
24
+ dirs.delete(Bowline.root.join("log").to_s)
25
+ dirs.delete(Bowline.root.join(*%w{db migrate}).to_s)
26
+ dirs.delete_if {|i| i =~ /\.svn|\.DS_Store|\.git/ }
27
+
28
+ Zip::ZipFile.open(app_path, Zip::ZipFile::CREATE) do |zf|
29
+ # This is horrible - but RubyZIP's API sucks
30
+ blank = Tempfile.new("blank")
31
+ zf.add("app_first_run", blank.path)
32
+ zf.add("app_production", blank.path)
33
+
34
+ dirs.each do |dir|
35
+ name = dir.sub(Bowline.root.to_s + "/", "")
36
+ zf.add(name, dir)
37
+ end
38
+ end
39
+ end
40
+
7
41
  task :osx => :environment do
8
42
  unless Bowline::Library.ready?
9
43
  Rake::Task["libs:setup"].invoke
@@ -11,7 +45,7 @@ namespace :app do
11
45
 
12
46
  config = Bowline.configuration
13
47
  assets_path = File.join(Bowline.assets_path, "osx")
14
- build_path = Bowline::Library.local_build_path
48
+ build_path = Bowline::Library.local_build_path.to_s
15
49
  app_path = File.join(build_path, "#{config.name}.app")
16
50
  FileUtils.rm_rf(app_path)
17
51
  contents_path = File.join(app_path, "Contents")
@@ -32,7 +66,7 @@ namespace :app do
32
66
  # Make icon
33
67
  makeicns = File.join(assets_path, "makeicns")
34
68
  if config.icon
35
- makeicns_in = File.expand_path(config.icon, APP_ROOT)
69
+ makeicns_in = File.expand_path(config.icon, Bowline.root)
36
70
  else
37
71
  makeicns_in = File.join(assets_path, "bowline.png")
38
72
  end
@@ -41,13 +75,15 @@ namespace :app do
41
75
  `#{makeicns} -in #{makeicns_in} -out #{makeicns_out}`
42
76
 
43
77
  # Copy App
44
- dirs = Dir[File.join(APP_ROOT, '**')]
78
+ dirs = Dir[File.join(APP_ROOT, "**")]
45
79
  dirs.delete(build_path)
46
- dirs.delete(File.join(APP_ROOT, 'log'))
47
- dirs.delete(File.join(APP_ROOT, 'tmp'))
48
- dirs.delete(File.join(APP_ROOT, 'db', 'migrate'))
49
- dirs.delete_if {|i| i =~ /\.svn|\.DS_Store/ }
50
- FileUtils.cp_r(dirs, '.')
80
+ dirs.delete(File.join(APP_ROOT, "log"))
81
+ dirs.delete(File.join(APP_ROOT, *%w{db migrate}))
82
+ dirs.delete_if {|i| i =~ /\.svn|\.DS_Store|\.git/ }
83
+ FileUtils.cp_r(dirs, ".")
84
+
85
+ FileUtils.touch("app_production")
86
+ FileUtils.touch("app_first_run")
51
87
  end
52
88
 
53
89
  # Copy Bowline binary & libs
@@ -34,14 +34,6 @@ def unzip(fpath, tpath)
34
34
  }
35
35
  end
36
36
 
37
- def sym_or_copy(from, to)
38
- begin
39
- FileUtils.ln_s(from, to)
40
- rescue NotImplementedError
41
- FileUtils.cp_r(from, to)
42
- end
43
- end
44
-
45
37
  namespace :libs do
46
38
  desc "Download Bowline's binary and pre-compiled libs"
47
39
  task :download => :environment do
@@ -2,7 +2,7 @@ module Bowline
2
2
  module Version #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 6
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
 
@@ -7,8 +7,4 @@ Bowline::Initializer.run do |config|
7
7
  config.version = "0.0.1"
8
8
  config.publisher = "Example"
9
9
  config.url = "http://example.com"
10
-
11
- # config.gem "activerecord"
12
- # config.gem "net-mdns", :lib => 'net/dns/mdns'
13
- # config.gem "rubyzip", :lib => 'zip/zip'
14
10
  end
@@ -1,5 +1,5 @@
1
1
  class MainWindow < Bowline::Desktop::WindowManager
2
- setup
2
+ setup!
3
3
  self.file = :index
4
4
  self.width = 300
5
5
  self.height = 400
@@ -1,2 +1,3 @@
1
1
  <%= shebang %>
2
- require File.join(*%w[config environment])
2
+ require File.join(File.dirname(__FILE__), *%w[.. config boot])
3
+ require "bowline/commands/init"
@@ -1,3 +1,3 @@
1
1
  class <%= class_name %> < Bowline::Desktop::WindowManager
2
- setup
2
+ setup!
3
3
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 6
8
- - 2
9
- version: 0.6.2
8
+ - 3
9
+ version: 0.6.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Alex MacCaw
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-02-25 00:00:00 +00:00
17
+ date: 2010-04-06 00:00:00 +01:00
18
18
  default_executable: bowline-gen
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -118,6 +118,7 @@ files:
118
118
  - lib/bowline/commands/build.rb
119
119
  - lib/bowline/commands/console.rb
120
120
  - lib/bowline/commands/generate.rb
121
+ - lib/bowline/commands/init.rb
121
122
  - lib/bowline/commands/run.rb
122
123
  - lib/bowline/desktop.rb
123
124
  - lib/bowline/desktop/app.rb
@@ -165,6 +166,8 @@ files:
165
166
  - templates/config/boot.rb
166
167
  - templates/config/database.yml
167
168
  - templates/config/environment.rb
169
+ - templates/config/environments/development.rb
170
+ - templates/config/environments/production.rb
168
171
  - templates/gitignore
169
172
  - templates/helper.rb
170
173
  - templates/main_window.rb