grippy-doozer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/.document +5 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +57 -0
  5. data/Rakefile +59 -0
  6. data/VERSION +1 -0
  7. data/bin/doozer +8 -0
  8. data/doozer.gemspec +114 -0
  9. data/lib/doozer/README.rb +40 -0
  10. data/lib/doozer/active_support/array.rb +14 -0
  11. data/lib/doozer/active_support/class.rb +221 -0
  12. data/lib/doozer/active_support/date_time.rb +23 -0
  13. data/lib/doozer/active_support/object.rb +43 -0
  14. data/lib/doozer/active_support/time.rb +32 -0
  15. data/lib/doozer/app.rb +265 -0
  16. data/lib/doozer/configs.rb +131 -0
  17. data/lib/doozer/controller.rb +335 -0
  18. data/lib/doozer/extend.rb +10 -0
  19. data/lib/doozer/initializer.rb +95 -0
  20. data/lib/doozer/lib.rb +32 -0
  21. data/lib/doozer/logger.rb +11 -0
  22. data/lib/doozer/orm/active_record.rb +19 -0
  23. data/lib/doozer/orm/data_mapper.rb +19 -0
  24. data/lib/doozer/orm/sequel.rb +18 -0
  25. data/lib/doozer/partial.rb +99 -0
  26. data/lib/doozer/plugins/paginate/init.rb +2 -0
  27. data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
  28. data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
  29. data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
  30. data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
  31. data/lib/doozer/rackup/server.ru +37 -0
  32. data/lib/doozer/rackup/test.rb +19 -0
  33. data/lib/doozer/redirect.rb +12 -0
  34. data/lib/doozer/route.rb +264 -0
  35. data/lib/doozer/scripts/cluster.rb +132 -0
  36. data/lib/doozer/scripts/migrate.rb +108 -0
  37. data/lib/doozer/scripts/task.rb +60 -0
  38. data/lib/doozer/scripts/test.rb +23 -0
  39. data/lib/doozer/version.rb +8 -0
  40. data/lib/doozer/view_helpers.rb +163 -0
  41. data/lib/doozer/watcher.rb +375 -0
  42. data/lib/doozer.rb +30 -0
  43. data/lib/generator/generator.rb +547 -0
  44. data/templates/skeleton/Rakefile +3 -0
  45. data/templates/skeleton/app/controllers/application_controller.rb +2 -0
  46. data/templates/skeleton/app/controllers/index_controller.rb +7 -0
  47. data/templates/skeleton/app/helpers/application_helper.rb +17 -0
  48. data/templates/skeleton/app/views/global/_header.html.erb +7 -0
  49. data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
  50. data/templates/skeleton/app/views/index/index.html.erb +108 -0
  51. data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
  52. data/templates/skeleton/config/app.yml +31 -0
  53. data/templates/skeleton/config/database.yml +25 -0
  54. data/templates/skeleton/config/environment.rb +13 -0
  55. data/templates/skeleton/config/rack.rb +30 -0
  56. data/templates/skeleton/config/routes.rb +69 -0
  57. data/templates/skeleton/script/cluster +5 -0
  58. data/templates/skeleton/script/migrate +5 -0
  59. data/templates/skeleton/script/task +5 -0
  60. data/templates/skeleton/script/test +4 -0
  61. data/templates/skeleton/static/404.html +16 -0
  62. data/templates/skeleton/static/500.html +16 -0
  63. data/templates/skeleton/static/css/style.css +32 -0
  64. data/templates/skeleton/static/favicon.ico +0 -0
  65. data/templates/skeleton/static/js/application.js +1 -0
  66. data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
  67. data/templates/skeleton/static/robots.txt +5 -0
  68. data/templates/skeleton/test/fixtures/setup.rb +6 -0
  69. data/templates/skeleton/test/setup.rb +33 -0
  70. data/test/doozer_test.rb +7 -0
  71. data/test/test_helper.rb +10 -0
  72. metadata +126 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Greg Melton
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,57 @@
1
+ = What is Doozer?
2
+ * A lightweight, ORM agnostic, Rack (http://rack.rubyforge.org) compatible MVC framework.
3
+
4
+ == Project Inspiration
5
+ * Doozer was initially a project I started working on for Google App Engine. It was eventually abandoned since GAE lacked (at the time and may still) proper support for migrations. I ported some of the code to Ruby mainly to learn the internals of Rack back in December 2008.
6
+ * Rails conventions and methodologies (mostly the application structure, configuration, nomenclature, scripts, and a few viewhelper methods).
7
+ * Clustering ala Mongrel::Cluster
8
+
9
+ == Requirements
10
+ * Ruby < 1.9 (untestested on Ruby 1.9 as of now)
11
+ * Rack (http://rack.rubyforge.org) gem (0.9.1 or 1.0.0)
12
+ * Any http server supported by Rack
13
+ * ActiveRecord, DataMapper or Sequel (only if you need DB support)
14
+
15
+ == Getting Started
16
+ 1. Install the gem(s):
17
+ - gem sources -a http://gems.github.com (if you haven't already)
18
+ - sudo gem install rack
19
+ - sudo gem install grippy-doozer
20
+ 2. Run 'doozer test-app' to generate the base application skeleton.
21
+ 3. Fire up the application in development mode
22
+ - cd test-app
23
+ - script/cluster -C start
24
+ 4. Navigate to http://localhost:9292
25
+
26
+ == Configuration
27
+ * Doozer is configurable to use an ORM library of your choosing: ActiveRecord, DataMapper, or Sequel.
28
+ * By default, routes are handled by Doozer controllers or you can define other Rack compatible applications which handle specific request paths.
29
+ * Http server of your liking (Mongrel, Thin, Lighttpd, or anything supported by Rack).
30
+ * Multiple appservers.
31
+ * Static directories.
32
+
33
+ == Useful Scripts
34
+ * Generate an application skeleton. Run 'doozer test-app' (see Getting Started).
35
+ * Generate views, controllers, and models (depending on the configured ORM). Run 'doozer generate -h' for more info.
36
+ * start/stop/restart your web server(s). Run 'script/cluster -h' for more info.
37
+ * Migrations up or down. 'script/migrate -h'.
38
+ * Tasks. Run 'script/task -h' for more info
39
+ * There is a rudimentary test command which allows you to run your own test suite for your application. Run 'script/test -h' for more info.
40
+
41
+ == Current limitations:
42
+ * Doozer has no test suite. Tsk-tsk I know. In the works.
43
+ * Right now, it doesn't keep track of migration versions.
44
+ * Magic routes are turned off. Still debating adding them back.
45
+ * Not all the documentation is in place.
46
+
47
+ == Note on Patches/Pull Requests
48
+ * Fork the project.
49
+ * Make your feature addition or bug fix.
50
+ * Add tests for it. This is important so I don't break it in a
51
+ future version unintentionally.
52
+ * Commit, do not mess with rakefile, version, or history.
53
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
54
+ * Send me a pull request. Bonus points for topic branches.
55
+
56
+ == Copyright
57
+ Copyright (c) 2009 Greg Melton. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "doozer"
8
+ gem.summary = %Q{A little MVC framework for Rack applications.}
9
+ gem.description = %Q{This GEM provides a small, barebones framework for creating MVC Rack applications.}
10
+ gem.email = "gmelton@whorde.com"
11
+ gem.homepage = "http://github.com/grippy/doozer"
12
+ gem.authors = ["grippy"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ gem.files.include %w() # lib/**/**/**/**/** templates/**/**/**/**
15
+
16
+ end
17
+
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+
43
+
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ if File.exist?('VERSION')
50
+ version = File.read('VERSION')
51
+ else
52
+ version = ""
53
+ end
54
+
55
+ rdoc.rdoc_dir = 'rdoc'
56
+ rdoc.title = "doozer #{version}"
57
+ rdoc.rdoc_files.include('README*')
58
+ rdoc.rdoc_files.include('lib/**/*.rb')
59
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/doozer ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6
+ require 'doozer'
7
+
8
+ Doozer::Generator.run(*ARGV)
data/doozer.gemspec ADDED
@@ -0,0 +1,114 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{doozer}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["grippy"]
12
+ s.date = %q{2009-08-09}
13
+ s.default_executable = %q{doozer}
14
+ s.description = %q{This GEM provides a small, barebones framework for creating MVC Rack applications.}
15
+ s.email = %q{gmelton@whorde.com}
16
+ s.executables = ["doozer"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/doozer",
29
+ "doozer.gemspec",
30
+ "lib/doozer.rb",
31
+ "lib/doozer/README.rb",
32
+ "lib/doozer/active_support/array.rb",
33
+ "lib/doozer/active_support/class.rb",
34
+ "lib/doozer/active_support/date_time.rb",
35
+ "lib/doozer/active_support/object.rb",
36
+ "lib/doozer/active_support/time.rb",
37
+ "lib/doozer/app.rb",
38
+ "lib/doozer/configs.rb",
39
+ "lib/doozer/controller.rb",
40
+ "lib/doozer/extend.rb",
41
+ "lib/doozer/initializer.rb",
42
+ "lib/doozer/lib.rb",
43
+ "lib/doozer/logger.rb",
44
+ "lib/doozer/orm/active_record.rb",
45
+ "lib/doozer/orm/data_mapper.rb",
46
+ "lib/doozer/orm/sequel.rb",
47
+ "lib/doozer/partial.rb",
48
+ "lib/doozer/plugins/paginate/init.rb",
49
+ "lib/doozer/plugins/paginate/lib/paginate.rb",
50
+ "lib/doozer/plugins/paginate/lib/paginate/collection.rb",
51
+ "lib/doozer/plugins/paginate/lib/paginate/finder.rb",
52
+ "lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb",
53
+ "lib/doozer/rackup/server.ru",
54
+ "lib/doozer/rackup/test.rb",
55
+ "lib/doozer/redirect.rb",
56
+ "lib/doozer/route.rb",
57
+ "lib/doozer/scripts/cluster.rb",
58
+ "lib/doozer/scripts/migrate.rb",
59
+ "lib/doozer/scripts/task.rb",
60
+ "lib/doozer/scripts/test.rb",
61
+ "lib/doozer/version.rb",
62
+ "lib/doozer/view_helpers.rb",
63
+ "lib/doozer/watcher.rb",
64
+ "lib/generator/generator.rb",
65
+ "templates/skeleton/Rakefile",
66
+ "templates/skeleton/app/controllers/application_controller.rb",
67
+ "templates/skeleton/app/controllers/index_controller.rb",
68
+ "templates/skeleton/app/helpers/application_helper.rb",
69
+ "templates/skeleton/app/views/global/_header.html.erb",
70
+ "templates/skeleton/app/views/global/_navigation.html.erb",
71
+ "templates/skeleton/app/views/index/index.html.erb",
72
+ "templates/skeleton/app/views/layouts/default.html.erb",
73
+ "templates/skeleton/config/app.yml",
74
+ "templates/skeleton/config/database.yml",
75
+ "templates/skeleton/config/environment.rb",
76
+ "templates/skeleton/config/rack.rb",
77
+ "templates/skeleton/config/routes.rb",
78
+ "templates/skeleton/script/cluster",
79
+ "templates/skeleton/script/migrate",
80
+ "templates/skeleton/script/task",
81
+ "templates/skeleton/script/test",
82
+ "templates/skeleton/static/404.html",
83
+ "templates/skeleton/static/500.html",
84
+ "templates/skeleton/static/css/style.css",
85
+ "templates/skeleton/static/favicon.ico",
86
+ "templates/skeleton/static/js/application.js",
87
+ "templates/skeleton/static/js/jquery-1.3.min.js",
88
+ "templates/skeleton/static/robots.txt",
89
+ "templates/skeleton/test/fixtures/setup.rb",
90
+ "templates/skeleton/test/setup.rb",
91
+ "test/doozer_test.rb",
92
+ "test/test_helper.rb"
93
+ ]
94
+ s.has_rdoc = true
95
+ s.homepage = %q{http://github.com/grippy/doozer}
96
+ s.rdoc_options = ["--charset=UTF-8"]
97
+ s.require_paths = ["lib"]
98
+ s.rubygems_version = %q{1.3.1}
99
+ s.summary = %q{A little MVC framework for Rack applications.}
100
+ s.test_files = [
101
+ "test/doozer_test.rb",
102
+ "test/test_helper.rb"
103
+ ]
104
+
105
+ if s.respond_to? :specification_version then
106
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
107
+ s.specification_version = 2
108
+
109
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
110
+ else
111
+ end
112
+ else
113
+ end
114
+ end
@@ -0,0 +1,40 @@
1
+ # = What is Doozer?
2
+ # * A lightweight, ORM agnostic, Rack (http://rack.rubyforge.org) compatible MVC framework.
3
+ #
4
+ # == Project Inspiration
5
+ # * Doozer was initially a project I started working on for Google App Engine. It was eventually abandoned since GAE lacked (at the time and may still) proper support for migrations. I ported some of the code to Ruby mainly to learn the internals of Rack back in December 2008.
6
+ # * Some of the more useful Rails conventions and methodologies (mostly the application structure, configuration, nonmenclature, scripts, and a few viewhelper methods).
7
+ # * Clustering ala Mongrel::Cluster
8
+ #
9
+ # == Requirements
10
+ # * Ruby < 1.9 (untestested on Ruby 1.9 as of now)
11
+ # * Rack (http://rack.rubyforge.org) gem (0.9.1 or 1.0.0)
12
+ # * An http server supported by Rack
13
+ # * ActiveRecord, DataMapper or Sequel
14
+ #
15
+ # == Getting Started
16
+ # 1. Create a folder called 'test' and checkout Doozer.
17
+ # 2. From the root 'test' directory run 'ruby doozer/commands/scaffold.rb' to generate the base application skeleton.
18
+ # 3. Fire up the application in development mode=> 'script/clusters -C start'.
19
+ # 4. Navigate to http://localhost:9292
20
+ #
21
+ # == Configuration
22
+ # * Doozer is configurable to use an ORM library of your choosing: ActiveRecord, DataMapper, or Sequel.
23
+ # * By default, routes are handled by Doozer controllers or you can define other Rack compatible applications which handle specific request paths.
24
+ # * Http server of your liking (Mongrel, Thin, Lighttpd, or anything supported by Rack).
25
+ # * Multiple appservers.
26
+ # * Static directories.
27
+ #
28
+ # == Useful Scripts
29
+ # * Generate an application skeleton. Run 'ruby doozer/commands/scaffold.rb' (see Getting Started).
30
+ # * Generate views, controllers, and models (depending on the configured ORM). Run 'script/generate -h' for more info.
31
+ # * start/stop/restart your web server(s). Run 'script/clusters -h' for more info.
32
+ # * Migrations up or down. 'script/migrate -h'.
33
+ # * Tasks. Run 'script/task -h' for more info
34
+ # * There is a rudimentary test command which allows you to run your own test suite for your application. Run 'script/test -h' for more info.
35
+ #
36
+ # == Current limitations:
37
+ # * Doozer has no test suite. Tsk-tsk I know. In the works.
38
+ # * Right now, it doesn't keep track of migration versions.
39
+ # * Magic routes are turned off. These worked at one point. They may work again one day.
40
+ # * No way to override a view template from inside an action.
@@ -0,0 +1,14 @@
1
+ class Array
2
+ # Extracts options from a set of arguments. Removes and returns the last
3
+ # element in the array if it's a hash, otherwise returns a blank hash.
4
+ #
5
+ # def options(*args)
6
+ # args.extract_options!
7
+ # end
8
+ #
9
+ # options(1, 2) # => {}
10
+ # options(1, 2, :a => :b) # => {:a=>:b}
11
+ def extract_options!
12
+ last.is_a?(::Hash) ? pop : {}
13
+ end
14
+ end
@@ -0,0 +1,221 @@
1
+ # Retain for backward compatibility. Methods are now included in Class.
2
+ module ClassInheritableAttributes # :nodoc:
3
+ end
4
+
5
+ # Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
6
+ # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
7
+ # to, for example, an array without those additions being shared with either their parent, siblings, or
8
+ # children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
9
+ class Class # :nodoc:
10
+ def class_inheritable_reader(*syms)
11
+ syms.each do |sym|
12
+ next if sym.is_a?(Hash)
13
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
14
+ def self.#{sym} # def self.after_add
15
+ read_inheritable_attribute(:#{sym}) # read_inheritable_attribute(:after_add)
16
+ end # end
17
+
18
+ def #{sym} # def after_add
19
+ self.class.#{sym} # self.class.after_add
20
+ end # end
21
+ EOS
22
+ end
23
+ end
24
+
25
+ def class_inheritable_writer(*syms)
26
+ options = syms.extract_options!
27
+ syms.each do |sym|
28
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
29
+ def self.#{sym}=(obj) # def self.color=(obj)
30
+ write_inheritable_attribute(:#{sym}, obj) # write_inheritable_attribute(:color, obj)
31
+ end # end
32
+ #
33
+ #{" #
34
+ def #{sym}=(obj) # def color=(obj)
35
+ self.class.#{sym} = obj # self.class.color = obj
36
+ end # end
37
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
38
+ EOS
39
+ end
40
+ end
41
+
42
+ def class_inheritable_array_writer(*syms)
43
+ options = syms.extract_options!
44
+ syms.each do |sym|
45
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
46
+ def self.#{sym}=(obj) # def self.levels=(obj)
47
+ write_inheritable_array(:#{sym}, obj) # write_inheritable_array(:levels, obj)
48
+ end # end
49
+ #
50
+ #{" #
51
+ def #{sym}=(obj) # def levels=(obj)
52
+ self.class.#{sym} = obj # self.class.levels = obj
53
+ end # end
54
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
55
+ EOS
56
+ end
57
+ end
58
+
59
+ def class_inheritable_hash_writer(*syms)
60
+ options = syms.extract_options!
61
+ syms.each do |sym|
62
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
63
+ def self.#{sym}=(obj) # def self.nicknames=(obj)
64
+ write_inheritable_hash(:#{sym}, obj) # write_inheritable_hash(:nicknames, obj)
65
+ end # end
66
+ #
67
+ #{" #
68
+ def #{sym}=(obj) # def nicknames=(obj)
69
+ self.class.#{sym} = obj # self.class.nicknames = obj
70
+ end # end
71
+ " unless options[:instance_writer] == false } # # the writer above is generated unless options[:instance_writer] == false
72
+ EOS
73
+ end
74
+ end
75
+
76
+ def class_inheritable_accessor(*syms)
77
+ class_inheritable_reader(*syms)
78
+ class_inheritable_writer(*syms)
79
+ end
80
+
81
+ def class_inheritable_array(*syms)
82
+ class_inheritable_reader(*syms)
83
+ class_inheritable_array_writer(*syms)
84
+ end
85
+
86
+ def class_inheritable_hash(*syms)
87
+ class_inheritable_reader(*syms)
88
+ class_inheritable_hash_writer(*syms)
89
+ end
90
+
91
+ def inheritable_attributes
92
+ @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
93
+ end
94
+
95
+ def write_inheritable_attribute(key, value)
96
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
97
+ @inheritable_attributes = {}
98
+ end
99
+ inheritable_attributes[key] = value
100
+ end
101
+
102
+ def write_inheritable_array(key, elements)
103
+ write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
104
+ write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
105
+ end
106
+
107
+ def write_inheritable_hash(key, hash)
108
+ write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
109
+ write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
110
+ end
111
+
112
+ def read_inheritable_attribute(key)
113
+ inheritable_attributes[key]
114
+ end
115
+
116
+ def reset_inheritable_attributes
117
+ @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
118
+ end
119
+
120
+ private
121
+ # Prevent this constant from being created multiple times
122
+ EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
123
+
124
+ def inherited_with_inheritable_attributes(child)
125
+ inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
126
+
127
+ if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
128
+ new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
129
+ else
130
+ new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
131
+ memo.update(key => value.duplicable? ? value.dup : value)
132
+ end
133
+ end
134
+
135
+ child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
136
+ end
137
+
138
+ alias inherited_without_inheritable_attributes inherited
139
+ alias inherited inherited_with_inheritable_attributes
140
+ end
141
+
142
+ class Class
143
+ # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
144
+ # each subclass has a copy of parent's attribute.
145
+ #
146
+ # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
147
+ # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
148
+ #
149
+ # @api public
150
+ #
151
+ # @todo Do we want to block instance_reader via :instance_reader => false
152
+ # @todo It would be preferable that we do something with a Hash passed in
153
+ # (error out or do the same as other methods above) instead of silently
154
+ # moving on). In particular, this makes the return value of this function
155
+ # less useful.
156
+ def extlib_inheritable_reader(*ivars)
157
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
158
+
159
+ ivars.each do |ivar|
160
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
161
+ def self.#{ivar}
162
+ return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
163
+ ivar = superclass.#{ivar}
164
+ return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
165
+ @#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) ? ivar.dup : ivar
166
+ end
167
+ RUBY
168
+ unless instance_reader == false
169
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
170
+ def #{ivar}
171
+ self.class.#{ivar}
172
+ end
173
+ RUBY
174
+ end
175
+ end
176
+ end
177
+
178
+ # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
179
+ # each subclass has a copy of parent's attribute.
180
+ #
181
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
182
+ # define inheritable writer for.
183
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
184
+ # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
185
+ #
186
+ # @api public
187
+ #
188
+ # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
189
+ # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
190
+ def extlib_inheritable_writer(*ivars)
191
+ instance_writer = ivars.pop[:writer] if ivars.last.is_a?(Hash)
192
+ ivars.each do |ivar|
193
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
194
+ def self.#{ivar}=(obj)
195
+ @#{ivar} = obj
196
+ end
197
+ RUBY
198
+ unless instance_writer == false
199
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
200
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
201
+ RUBY
202
+ end
203
+
204
+ self.send("#{ivar}=", yield) if block_given?
205
+ end
206
+ end
207
+
208
+ # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
209
+ # each subclass has a copy of parent's attribute.
210
+ #
211
+ # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
212
+ # define inheritable accessor for.
213
+ # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
214
+ # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
215
+ #
216
+ # @api public
217
+ def extlib_inheritable_accessor(*syms, &block)
218
+ extlib_inheritable_reader(*syms)
219
+ extlib_inheritable_writer(*syms, &block)
220
+ end
221
+ end
@@ -0,0 +1,23 @@
1
+ # Default DATE_FORMATS available
2
+ #
3
+ # Time::DATE_FORMATS.merge!({
4
+ # :db => "%Y-%m-%d %H:%M:%S",
5
+ # :number => "%Y%m%d%H%M%S",
6
+ # :time => "%H:%M",
7
+ # :mdy => "%B %d, %Y",
8
+ # :short => "%d %b %H:%M",
9
+ # :long => "%B %d, %Y %H:%M"
10
+ # })
11
+ # === Example Useage
12
+ # DateTime.now().to_format(:mdy)
13
+ #
14
+ class DateTime
15
+ # Helper method for string formatting DateTime.
16
+ def to_format(key = :default)
17
+ if format = ::Time::DATE_FORMATS[key]
18
+ strftime(format)
19
+ else
20
+ to_s
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,43 @@
1
+ class Object
2
+ # Can you safely .dup this object?
3
+ # False for nil, false, true, symbols, numbers, and class objects; true otherwise.
4
+ def duplicable?
5
+ true
6
+ end
7
+ end
8
+
9
+ class NilClass #:nodoc:
10
+ def duplicable?
11
+ false
12
+ end
13
+ end
14
+
15
+ class FalseClass #:nodoc:
16
+ def duplicable?
17
+ false
18
+ end
19
+ end
20
+
21
+ class TrueClass #:nodoc:
22
+ def duplicable?
23
+ false
24
+ end
25
+ end
26
+
27
+ class Symbol #:nodoc:
28
+ def duplicable?
29
+ false
30
+ end
31
+ end
32
+
33
+ class Numeric #:nodoc:
34
+ def duplicable?
35
+ false
36
+ end
37
+ end
38
+
39
+ class Class #:nodoc:
40
+ def duplicable?
41
+ false
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ # Default DATE_FORMATS available
2
+ #
3
+ # Time::DATE_FORMATS.merge!({
4
+ # :db => "%Y-%m-%d %H:%M:%S",
5
+ # :number => "%Y%m%d%H%M%S",
6
+ # :time => "%H:%M",
7
+ # :mdy => "%B %d, %Y",
8
+ # :short => "%d %b %H:%M",
9
+ # :long => "%B %d, %Y %H:%M"
10
+ # })
11
+ # === Example Useage
12
+ # DateTime.now().to_format(:mdy)
13
+ #
14
+ class Time
15
+ # Helper method for string formatting Time.
16
+ DATE_FORMATS = {
17
+ :db => "%Y-%m-%d %H:%M:%S",
18
+ :number => "%Y%m%d%H%M%S",
19
+ :time => "%H:%M",
20
+ :mdy => "%B %d, %Y",
21
+ :short => "%d %b %H:%M",
22
+ :long => "%B %d, %Y %H:%M"
23
+ }
24
+
25
+ def to_format(key = :default)
26
+ if format = ::Time::DATE_FORMATS[key]
27
+ strftime(format)
28
+ else
29
+ to_s
30
+ end
31
+ end
32
+ end