bowline 0.6.2 → 0.6.3

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