app 0.2.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ === 0.9.0 / 2010-02-21
2
+
3
+ * 1 major enhancement
4
+
5
+ * Rebirthday!
6
+
7
+
1
8
  === 0.2.2 / 2009-04-17
2
9
 
3
10
  * 2 minor enhancements
@@ -25,6 +32,7 @@
25
32
  * Removed superfluous "init" and "uninstall" files (handle load errors more
26
33
  gracefully in "app", and let "script/destroy" do its thing).
27
34
 
35
+
28
36
  === 0.1.2 / 2009-04-02
29
37
 
30
38
  * 1 minor enhancement
@@ -1,127 +1,129 @@
1
1
  = App
2
2
 
3
- http://github.com/stephencelis/app
4
-
5
-
6
- == DESCRIPTION
7
-
8
3
  Move the config out of your app, and into App.
9
4
 
10
- Sure, it's been done before, and others will do it again, but this is my way,
11
- and I like it.
12
-
13
-
14
- == FEATURES/PROBLEMS
15
-
16
- * Easy, environmentally-friendly configuration access.
17
-
18
-
19
- For mutability, try acts_as_singleton or KVC:
20
-
21
- * http://github.com/stephencelis/acts_as_singleton
22
- * http://github.com/stephencelis/kvc
23
-
24
-
25
- == SYNOPSIS
26
-
27
- App looks for and loads configuration from "config/app.yml", providing a
28
- namespaced API for access.
29
-
30
- App.config # => {"apis"=>{"flickr"=>{ ... }}
31
-
32
-
33
- Sugar is always sweeter:
34
-
35
- App.config("apis", "flickr") # => App.config["apis"]["flickr"]
36
-
37
-
38
- Who doesn't like sugar?
39
5
 
40
- App["apis", "flickr"]
6
+ == WARNING:
41
7
 
8
+ This release is new, improved, and almost completely incompatible with the
9
+ gem formerly known as App. (See "What about YAML?", below.)
42
10
 
43
- Sugar, sugar, sugar.
44
11
 
45
- App.apis("flickr")
12
+ == K.
46
13
 
14
+ === Rails (3.0)
47
15
 
48
- Let's not overdo it, though. <tt>App.apis.flickr</tt> just doesn't look right.
16
+ % echo 'gem "app"' >> Gemfile
17
+ % bundle install
18
+ % rails g configurable
49
19
 
50
20
 
51
- == REQUIREMENTS
21
+ You now have App config files at config/app. It will generate an override per
22
+ Rails environment.
52
23
 
53
- * Rails 2.3.2 or greater.
24
+ # config/app.rb
25
+ class App < Configurable
26
+ config.javascript_expansions = {
27
+ :prototype => "prototype"
28
+ :scriptaculous => %w(effects dragdrop controls)
29
+ }
30
+ end
54
31
 
55
32
 
56
- == INSTALL
33
+ Need to update those sources for production?
57
34
 
58
- === With a template:
35
+ # config/app/production.rb
36
+ App.configure do
37
+ ajax_lib_base = "http://ajax.googleapis.com/ajax/libs"
59
38
 
60
- In existing projects:
39
+ config.javascript_expansions.update({
40
+ :prototype => "#{ajax_lib_base}/prototype/1.6.1.0/prototype.js"
41
+ :scriptaculous => "#{ajax_lib_base}/scriptaculous/1.8.3/scriptaculous.js"
42
+ })
43
+ end
61
44
 
62
- % rake rails:template LOCATION=http://gist.github.com/97629.txt
63
45
 
46
+ === Ruby
64
47
 
65
- For new projects:
48
+ App can be used in any Ruby application. Just bundle (see "Rails", above, less
49
+ the `rails g`) or install the gem.
66
50
 
67
- % rails newapp -m http://gist.github.com/97629.txt
51
+ Make sure "app" is required in your environment, and define a class from there.
68
52
 
53
+ class MyConfig < Configurable
54
+ config.launched_at = Time.now.utc
55
+ end
69
56
 
70
- === Or as a gem:
71
57
 
72
- Configure:
58
+ == What about YAML?
73
59
 
74
- # config/environment.rb
75
- config.gem "stephencelis-app", :lib => "app",
76
- :source => "http://gems.github.com",
77
- :version => ">= 0.2.2"
60
+ YAML is great, really, but it just wasn't great beyond simple configuration.
78
61
 
79
- And install:
62
+ Take that last example. ERB would be required, and still it's cumbersome.
80
63
 
81
- % sudo rake gems:install
64
+ # app.yml
65
+ ---
66
+ launched_at: <%= Time.now.utc.iso8601 %>
82
67
 
83
68
 
84
- === Or as a plugin:
69
+ Forget the ISO 8601 formatting and you'll get a string back, instead.
85
70
 
86
- Install:
71
+ But that's just the surface. Where you're manipulating the primitive objects
72
+ that YAML supports most easily, you could have stored complex Ruby objects:
73
+ Pathname, Proc, or anything else.
87
74
 
88
- % script/plugin install git://github.com/stephencelis/app.git
75
+ class App < Configurable
76
+ config.config_path = Rails.root.join("config")
77
+ config.uptime = Proc.new { (Time.now.utc - App.launched_at).seconds }
78
+ config.asset_host = ActiveSupport::StringInquirer.new "amazon"
79
+ end
89
80
 
81
+ App.config_path.join("app") # => #<Pathname:config/app>
82
+ App.uptime.call # => 430.198400 seconds
83
+ App.asset_host.amazon? # => true
90
84
 
91
- Or submodule:
92
85
 
93
- % git submodule add git://github.com/stephencelis/app.git vendor/plugins/app
86
+ And because these configuration files are just class definitions, you can
87
+ write your own methods, as well.
94
88
 
89
+ class App < Configurable
90
+ def self.destroy_sessions!
91
+ Session.delete_all
92
+ end
93
+ end
95
94
 
96
- === Finally, generate:
95
+ App.destroy_sessions! # => 1
97
96
 
98
- % script/generate app_config
99
97
 
98
+ If Ruby can handle it, so can App.
100
99
 
101
- If your "app.yml" gets out of hand, modularize, like in this heavy-handed
102
- example:
100
+ But OK, OK, there's still an upgrade path:
103
101
 
104
- % script/generate app_config apis/twitter
105
- create config/app/apis
106
- create config/app/apis/twitter.yml # Configure me!
102
+ class App < Configurable
103
+ # This must come first.
104
+ self.config = <<YAML
105
+ legacy: <%= "configuration" %>
106
+ YAML
107
107
 
108
+ # config.key = "value"
109
+ end
108
110
 
109
- Namespaces avoid collisions:
110
111
 
111
- App::Apis::Twitter.username
112
+ This is not backwards-compatible. This is only to help ease the transition.
113
+ You will most likely have to upgrade your calls.
112
114
 
113
115
 
114
116
  == LICENSE
115
117
 
116
118
  (The MIT License)
117
119
 
118
- (c) 2009-* Stephen Celis, stephen@stephencelis.com.
120
+ (c) 2009-2010 Stephen Celis, stephen@stephencelis.com.
119
121
 
120
- Permission is hereby granted, free of charge, to any person obtaining a copy
121
- of this software and associated documentation files (the "Software"), to deal
122
- in the Software without restriction, including without limitation the rights
123
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
124
- copies of the Software, and to permit persons to whom the Software is
122
+ Permission is hereby granted, free of charge, to any person obtaining a copy
123
+ of this software and associated documentation files (the "Software"), to deal
124
+ in the Software without restriction, including without limitation the rights
125
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
126
+ copies of the Software, and to permit persons to whom the Software is
125
127
  furnished to do so, subject to the following conditions:
126
128
 
127
129
  The above copyright notice and this permission notice shall be included in all
data/Rakefile CHANGED
@@ -1,23 +1,10 @@
1
- require 'rake'
2
1
  require 'rake/testtask'
3
- require 'rake/rdoctask'
4
2
 
5
3
  desc 'Default: run unit tests.'
6
4
  task :default => :test
7
5
 
8
6
  desc 'Test the app plugin.'
9
7
  Rake::TestTask.new(:test) do |t|
10
- t.libs << 'lib'
11
8
  t.libs << 'test'
12
9
  t.pattern = 'test/**/*_test.rb'
13
- t.verbose = true
14
- end
15
-
16
- desc 'Generate documentation for the app plugin.'
17
- Rake::RDocTask.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'App'
20
- rdoc.options << '--line-numbers' << '--inline-source'
21
- rdoc.rdoc_files.include('README.rdoc')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
23
10
  end
data/lib/app.rb CHANGED
@@ -1,73 +1,91 @@
1
- # App is your app.
1
+ # Provides configuration via superclass. Inherit from Configurable, and you
2
+ # have a handy little DSL/class for assigning/accessing your app settings.
2
3
  #
3
- # What would your app be without it? Still an app, but without App.
4
- module App
5
- VERSION = "0.2.2"
4
+ # class App < Configurable
5
+ # config.launched_at = Time.now.utc
6
+ # end
7
+ #
8
+ # App.launched_at # => 2010-02-21 12:34:56 UTC
9
+ #
10
+ # Configurable classes will warn you when you call undefined settings.
11
+ #
12
+ # App.typo # => nil
13
+ # warning: undefined setting `typo' for App:Class
14
+ #
15
+ # If you don't care about these warnings, just redefine the logger.
16
+ #
17
+ # App.configure do
18
+ # config.logger = Logger.new nil
19
+ # end
20
+ class Configurable
21
+ autoload :Logger, "logger"
22
+ autoload :OpenStruct, "ostruct"
23
+
24
+ # Deprecated autoloads.
25
+ autoload :ERB, "erb"
26
+ autoload :YAML, "yaml"
6
27
 
7
- @@config = {} # Initialize.
8
28
  class << self
9
- # Returns the application configuration hash, as defined in
10
- # "config/app.yml".
11
- #
12
- # Follows args through the hash tree. E.g.:
13
- #
14
- # App.config("apis", "flickr") # => config_hash["apis"]["flickr"]
15
- #
16
- # <tt>App.config</tt> is aliased to <tt>App.[]</tt>, so shorten things up:
17
- #
18
- # App["apis", "flickr"]
19
- #
20
- # Or rely on +method_missing+ to make it even shorter (and sweeter):
21
- #
22
- # App.apis("flickr")
23
- def config(*args)
24
- @@config if args.empty?
25
- args.inject(@@config) { |config, arg| config[arg] }
29
+ alias __name__ name
30
+ undef name
31
+
32
+ alias configure class_eval
33
+
34
+ def [](key)
35
+ unless config.respond_to? key
36
+ logger.warn "warning: undefined setting `#{key}' for #{__name__}:Class"
37
+ end
38
+
39
+ config.send key
26
40
  end
27
- alias [] config
28
41
 
29
42
  def inspect
30
- "#<#{name}: #{config.inspect}>"
43
+ config.inspect.sub config.class.name, __name__
44
+ end
45
+
46
+ def logger
47
+ return config.logger if config.respond_to? :logger
48
+ @logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDERR)
31
49
  end
32
50
 
33
51
  private
34
52
 
35
- def method_missing(method, *args)
36
- self[method.to_s, *args] || self[method, *args]
53
+ def config
54
+ @config ||= OpenStruct.new
37
55
  end
38
- end
39
-
40
- begin
41
- raw = File.read Rails.root.join("config", "#{name.underscore}.yml")
42
- all = YAML.load ERB.new(raw).result
43
- @@config = all[Rails.env] || all
44
- @@config.freeze
45
- rescue Errno::ENOENT => e
46
- puts '** App: no file "config/app.yml". Run `script/generate app_config`.'
47
- end
48
- end
49
56
 
50
- unless __FILE__ == "(eval)"
51
- module App
52
- class << self
53
- # Returns the name of the web application.
54
- def to_s
55
- File.basename Rails.root
57
+ def config=(settings)
58
+ @config = case settings
59
+ when String
60
+ logger.warn "warning: YAML is deprecated (#{__FILE__}:#{__LINE__})"
61
+ OpenStruct.new YAML.load(ERB.new(settings).result)
62
+ when Hash
63
+ OpenStruct.new settings
64
+ else
65
+ settings
56
66
  end
57
67
  end
68
+
69
+ def method_missing(method, *args, &block)
70
+ super if (key = method.to_s).end_with?("=")
71
+ boolean = key.chomp!("?")
72
+ value = self[key]
73
+ value = value.call(*args, &block) if value.respond_to?(:call)
74
+ boolean ? !!value : value
75
+ end
58
76
  end
59
77
 
60
- # Iterate through other App configs and namespace them.
61
- Dir[Rails.root.join("config", "app", "**/*.yml")].sort.each do |config|
62
- name = config.gsub(/#{Rails.root.join("config")}\/|\.yml/) {}.classify
78
+ if defined? Rails
79
+ class Plugin < Rails::Railtie # :nodoc:
80
+ railtie_name :configurable
63
81
 
64
- # Recognize all parents.
65
- line = name.split "::"
66
- line.inject line.shift do |parentage, descendant|
67
- eval "module #{parentage}; end"
68
- "#{parentage}::#{descendant}"
82
+ initializer "configurable.require_app" do |app|
83
+ begin
84
+ require app.root.join "config", "app"
85
+ require app.root.join "config", "app", Rails.env
86
+ rescue LoadError
87
+ end
88
+ end
69
89
  end
70
-
71
- eval File.read(__FILE__).sub("module App", "module #{name}")
72
90
  end
73
91
  end
@@ -0,0 +1,13 @@
1
+ class ConfigurableGenerator < Rails::Generators::Base # :nodoc:
2
+ def self.source_root
3
+ @_configurable_source_root ||=
4
+ File.expand_path("../#{base_name}/templates", __FILE__)
5
+ end
6
+
7
+ def install
8
+ copy_file "app.rb", "config/app.rb"
9
+ Dir[Rails.root.join("config", "environments", "*.rb").to_s].each do |env|
10
+ template "app/env.rb", "config/app/#{File.basename env}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ class App < Configurable # :nodoc:
2
+ # Settings in config/app/* take precedence over those specified here.
3
+ config.name = Rails::Application.instance.class.parent.name
4
+
5
+ # config.key = "value"
6
+ end
@@ -0,0 +1,5 @@
1
+ App.configure do
2
+ # Settings specified here will take precedence over those in config/app.rb
3
+
4
+ # config.key = "value"
5
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Celis
@@ -9,43 +9,27 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-17 00:00:00 -05:00
12
+ date: 2010-02-21 00:00:00 -06:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: hoe
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.12.0
24
- version:
25
- description: Move the config out of your app, and into App. Sure, it's been done before, and others will do it again, but this is my way, and I like it.
26
- email:
27
- - stephen@stephencelis.com
14
+ dependencies: []
15
+
16
+ description: Move the config out of your app, and into App.
17
+ email: stephen@stephencelis.com
28
18
  executables: []
29
19
 
30
20
  extensions: []
31
21
 
32
22
  extra_rdoc_files:
33
23
  - History.rdoc
34
- - Manifest.txt
35
24
  - README.rdoc
36
25
  files:
37
26
  - History.rdoc
38
- - Manifest.txt
39
27
  - README.rdoc
40
28
  - Rakefile
41
- - generators/app_config/app_config_generator.rb
42
- - generators/app_config/templates/app.yml
43
- - install.rb
44
29
  - lib/app.rb
45
- - test/app_test.rb
46
- - test/fixtures/app/authenticate/basic/config.yml
47
- - test/fixtures/app/authenticate.yml
48
- - test/fixtures/app.yml
30
+ - lib/generators/configurable_generator/templates/app/env.rb
31
+ - lib/generators/configurable_generator/templates/app.rb
32
+ - lib/generators/configurable_generator.rb
49
33
  has_rdoc: true
50
34
  homepage: http://github.com/stephencelis/app
51
35
  licenses: []
@@ -70,10 +54,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
54
  version:
71
55
  requirements: []
72
56
 
73
- rubyforge_project: app
57
+ rubyforge_project:
74
58
  rubygems_version: 1.3.5
75
59
  signing_key:
76
60
  specification_version: 3
77
- summary: Move the config out of your app, and into App
61
+ summary: Application configuration.
78
62
  test_files: []
79
63
 
@@ -1,12 +0,0 @@
1
- History.rdoc
2
- Manifest.txt
3
- README.rdoc
4
- Rakefile
5
- generators/app_config/app_config_generator.rb
6
- generators/app_config/templates/app.yml
7
- install.rb
8
- lib/app.rb
9
- test/app_test.rb
10
- test/fixtures/app/authenticate/basic/config.yml
11
- test/fixtures/app/authenticate.yml
12
- test/fixtures/app.yml
@@ -1,32 +0,0 @@
1
- class AppConfigGenerator < Rails::Generator::Base
2
- def manifest
3
- record do |m|
4
- if ARGV.empty?
5
- if File.exist? Rails.root.join("config", "app.yml")
6
- show_banner
7
- else
8
- m.file "app.yml", "config/app.yml"
9
- end
10
- else
11
- ARGV.each do |arg|
12
- path = "config/app/#{arg.underscore}"
13
- m.directory File.dirname(path)
14
- m.file "app.yml", "#{path}.yml"
15
- end
16
- end
17
- end
18
- end
19
-
20
- private
21
-
22
- def show_banner
23
- puts "App: you already have an app.yml!"
24
- puts
25
- puts " Remember to pass arguments to generate new configurations:"
26
- puts " script/generate app_config apis/twitter"
27
- puts
28
- puts " Generates:"
29
- puts " config/app/apis"
30
- puts " config/app/apis/twitter.yml"
31
- end
32
- end
@@ -1,18 +0,0 @@
1
- # An app config, provided by App: http://github.com/stephencelis/app
2
- ---
3
- development: &development
4
- loaded_at: <%= Time.zone.now.iso8601 %>
5
- apis:
6
- braintree:
7
- :login: testapi
8
- :password: password1
9
-
10
- test:
11
- <<: *development
12
-
13
- production:
14
- loaded_at: <%= Time.zone.now.iso8601 %>
15
- apis:
16
- braintree:
17
- :login: testapi
18
- :password: password1
data/install.rb DELETED
@@ -1 +0,0 @@
1
- $stdout.puts 'Run `script/generate app_config` to generate "config/app.yml".'
@@ -1,64 +0,0 @@
1
- require 'test/unit'
2
- require 'rubygems'
3
- require 'active_support'
4
- require 'active_support/test_case'
5
- require 'mocha'
6
- require 'erb'
7
-
8
- # Mock!
9
- module Rails
10
- def self.root
11
- self
12
- end
13
- def self.join(*args)
14
- args.shift # No "config" dir, OK?
15
- File.expand_path File.join(File.dirname(__FILE__), "fixtures", *args)
16
- end
17
- def self.env
18
- "development"
19
- end
20
- end
21
-
22
- # And load!
23
- require 'app'
24
-
25
- class AppTest < ActiveSupport::TestCase
26
- test "should access many ways" do
27
- assert_equal "Welcome!", App.config["welcome_message"]
28
- assert_equal "Welcome!", App.config("welcome_message")
29
- assert_equal "Welcome!", App["welcome_message"]
30
- assert_equal "Welcome!", App.welcome_message
31
-
32
- assert_equal "testapi", App.config["apis"]["braintree"][:login]
33
- assert_equal "testapi", App.config("apis", "braintree", :login)
34
- assert_equal "testapi", App["apis", "braintree", :login]
35
- assert_equal "testapi", App.apis("braintree", :login)
36
- end
37
-
38
- test "should parse ERB" do
39
- assert_instance_of Time, App.loaded_at
40
- end
41
-
42
- test "should accept boolean keys" do
43
- assert !App.process_payments?
44
- end
45
-
46
- test "should infer App.name" do
47
- File.stubs(:basename).returns "root"
48
- assert_equal "root", App.to_s
49
- end
50
-
51
- test "should namespace configs" do
52
- assert_instance_of Module, App::Authenticate
53
- assert_equal "frobozz", App::Authenticate["Stephen"]
54
- end
55
-
56
- test "should nest multiple levels of configs" do
57
- assert_instance_of Module, App::Authenticate::Basic::Config
58
- assert_equal :basic, App::Authenticate::Basic::Config.authentication_type
59
- end
60
-
61
- test "should be frozen" do
62
- assert_raise(TypeError) { App.config["loaded_at"] = Time.now }
63
- end
64
- end
@@ -1,9 +0,0 @@
1
- ---
2
- development:
3
- loaded_at: <%= Time.now.iso8601 %>
4
- welcome_message: Welcome!
5
- process_payments?: false
6
- apis:
7
- braintree:
8
- :login: testapi
9
- :password: password1
@@ -1,4 +0,0 @@
1
- ---
2
- development:
3
- Stephen: frobozz
4
- housemd: God
@@ -1,2 +0,0 @@
1
- ---
2
- authentication_type: :basic