doozer 0.2.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 (104) 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 +62 -0
  6. data/VERSION +1 -0
  7. data/bin/doozer +6 -0
  8. data/doozer.gemspec +156 -0
  9. data/lib/doozer.rb +35 -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 +294 -0
  16. data/lib/doozer/configs.rb +146 -0
  17. data/lib/doozer/controller.rb +340 -0
  18. data/lib/doozer/exceptions.rb +12 -0
  19. data/lib/doozer/extend.rb +10 -0
  20. data/lib/doozer/initializer.rb +104 -0
  21. data/lib/doozer/lib.rb +32 -0
  22. data/lib/doozer/logger.rb +12 -0
  23. data/lib/doozer/orm/active_record.rb +28 -0
  24. data/lib/doozer/orm/data_mapper.rb +22 -0
  25. data/lib/doozer/orm/sequel.rb +21 -0
  26. data/lib/doozer/partial.rb +99 -0
  27. data/lib/doozer/plugins/paginate/init.rb +2 -0
  28. data/lib/doozer/plugins/paginate/lib/paginate.rb +32 -0
  29. data/lib/doozer/plugins/paginate/lib/paginate/collection.rb +60 -0
  30. data/lib/doozer/plugins/paginate/lib/paginate/finder.rb +116 -0
  31. data/lib/doozer/plugins/paginate/lib/paginate/view_helpers.rb +37 -0
  32. data/lib/doozer/rackup/server.ru +35 -0
  33. data/lib/doozer/rackup/test.rb +20 -0
  34. data/lib/doozer/redirect.rb +12 -0
  35. data/lib/doozer/route.rb +292 -0
  36. data/lib/doozer/scripts/cluster.rb +126 -0
  37. data/lib/doozer/scripts/console.rb +2 -0
  38. data/lib/doozer/scripts/migrate.rb +108 -0
  39. data/lib/doozer/scripts/task.rb +60 -0
  40. data/lib/doozer/scripts/test.rb +23 -0
  41. data/lib/doozer/version.rb +8 -0
  42. data/lib/doozer/view_helpers.rb +251 -0
  43. data/lib/doozer/watcher.rb +369 -0
  44. data/lib/generator/generator.rb +548 -0
  45. data/templates/skeleton/Rakefile +3 -0
  46. data/templates/skeleton/app/controllers/application_controller.rb +2 -0
  47. data/templates/skeleton/app/controllers/index_controller.rb +7 -0
  48. data/templates/skeleton/app/helpers/application_helper.rb +17 -0
  49. data/templates/skeleton/app/views/global/_header.html.erb +7 -0
  50. data/templates/skeleton/app/views/global/_navigation.html.erb +6 -0
  51. data/templates/skeleton/app/views/index/index.html.erb +108 -0
  52. data/templates/skeleton/app/views/layouts/default.html.erb +23 -0
  53. data/templates/skeleton/config/app.yml +31 -0
  54. data/templates/skeleton/config/boot.rb +17 -0
  55. data/templates/skeleton/config/database.yml +25 -0
  56. data/templates/skeleton/config/environment.rb +11 -0
  57. data/templates/skeleton/config/rack.rb +30 -0
  58. data/templates/skeleton/config/routes.rb +69 -0
  59. data/templates/skeleton/script/cluster +4 -0
  60. data/templates/skeleton/script/console +15 -0
  61. data/templates/skeleton/script/migrate +4 -0
  62. data/templates/skeleton/script/task +4 -0
  63. data/templates/skeleton/script/test +4 -0
  64. data/templates/skeleton/static/404.html +16 -0
  65. data/templates/skeleton/static/500.html +16 -0
  66. data/templates/skeleton/static/css/style.css +32 -0
  67. data/templates/skeleton/static/favicon.ico +0 -0
  68. data/templates/skeleton/static/js/application.js +1 -0
  69. data/templates/skeleton/static/js/jquery-1.3.min.js +19 -0
  70. data/templates/skeleton/static/robots.txt +5 -0
  71. data/templates/skeleton/test/fixtures/setup.rb +6 -0
  72. data/templates/skeleton/test/setup.rb +33 -0
  73. data/test/doozer_test.rb +7 -0
  74. data/test/project/Rakefile +3 -0
  75. data/test/project/app/controllers/application_controller.rb +2 -0
  76. data/test/project/app/controllers/index_controller.rb +7 -0
  77. data/test/project/app/helpers/application_helper.rb +17 -0
  78. data/test/project/app/views/global/_header.html.erb +7 -0
  79. data/test/project/app/views/global/_navigation.html.erb +6 -0
  80. data/test/project/app/views/index/index.html.erb +108 -0
  81. data/test/project/app/views/layouts/default.html.erb +23 -0
  82. data/test/project/config/app.yml +31 -0
  83. data/test/project/config/boot.rb +17 -0
  84. data/test/project/config/database.yml +25 -0
  85. data/test/project/config/environment.rb +11 -0
  86. data/test/project/config/rack.rb +30 -0
  87. data/test/project/config/routes.rb +72 -0
  88. data/test/project/script/cluster +4 -0
  89. data/test/project/script/console +15 -0
  90. data/test/project/script/migrate +4 -0
  91. data/test/project/script/task +4 -0
  92. data/test/project/script/test +4 -0
  93. data/test/project/static/404.html +16 -0
  94. data/test/project/static/500.html +16 -0
  95. data/test/project/static/css/style.css +32 -0
  96. data/test/project/static/favicon.ico +0 -0
  97. data/test/project/static/js/application.js +1 -0
  98. data/test/project/static/js/jquery-1.3.min.js +19 -0
  99. data/test/project/static/robots.txt +5 -0
  100. data/test/project/test/fixtures/setup.rb +6 -0
  101. data/test/project/test/setup.rb +33 -0
  102. data/test/routing_test.rb +66 -0
  103. data/test/test_helper.rb +26 -0
  104. metadata +169 -0
data/lib/doozer/lib.rb ADDED
@@ -0,0 +1,32 @@
1
+ module Doozer
2
+ class Lib
3
+
4
+ #Return a ClassName as string from an underscored string.
5
+ # example: input "example_class" > "ExampleClass"
6
+ def self.classify(klass)
7
+ if klass.index('_')
8
+ klass = klass.split('_')
9
+ parts = []
10
+ klass = klass.each { | part |
11
+ parts.push(part.capitalize)
12
+ }
13
+ klass = parts.join('')
14
+ else
15
+ klass.capitalize!
16
+ end
17
+ end
18
+
19
+ #Returns a one-level deep folder/file structure and preservers underscores for filename.
20
+ # example: input "folder_some_file_name" > "folder/some_file_name"
21
+ def self.pathify_first(s)
22
+ if s.index('_')
23
+ parts = s.split('_')
24
+ folder = parts[0]
25
+ file = parts.slice(1, parts.length).join('_')
26
+ s = "#{folder}/#{file}"
27
+ end
28
+ s
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ module Doozer
2
+ module Util
3
+ module Logger
4
+
5
+ # Helper function for returning logger.
6
+ def logger
7
+ Doozer::Configs.logger
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ require 'active_record'
2
+ module Doozer
3
+ module ORM
4
+ def self.load
5
+ db_config = Doozer::Configs.db()
6
+ #ActiveRecord::Base.allow_concurrency = true
7
+ config = {
8
+ :adapter => db_config["adapter"],
9
+ :host => db_config["host"],
10
+ :username => db_config["username"],
11
+ :password => db_config["password"],
12
+ :database => db_config["database"]
13
+ }
14
+ config[:pool] = db_config["pool"] if db_config["pool"]
15
+ config[:reconnect] = db_config["reconnect"] if db_config["reconnect"]
16
+
17
+ ActiveRecord::Base.establish_connection(config)
18
+ printf "ORM: #{Doozer::Configs.orm()} initialized...\n"
19
+ # printf "ORM: logging initialized"
20
+ ActiveRecord::Base.logger = Doozer::Configs.logger
21
+ end
22
+
23
+ def self.after_request
24
+ ActiveRecord::Base.clear_active_connections!
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ require 'dm-core'
2
+ module Doozer
3
+
4
+ # See for more info => http://datamapper.org/doku.php?id=getting_started_with_datamapper
5
+ module ORM
6
+ def self.load
7
+ db_config = Doozer::Configs.db()
8
+ DataMapper.setup(:default, {
9
+ :adapter => db_config["adapter"],
10
+ :database => db_config["database"],
11
+ :username => db_config["username"],
12
+ :password => db_config["password"],
13
+ :host => db_config["host"]
14
+ })
15
+ printf "ORM: #{Doozer::Configs.orm()} initialized...\n"
16
+ DataMapper::Logger.new(STDOUT, :debug)
17
+ end
18
+
19
+ def self.after_request; end
20
+
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ require 'sequel'
2
+ module Doozer
3
+ module ORM
4
+
5
+ # See for details => http://sequel.rubyforge.org/rdoc/index.html
6
+ def self.load
7
+ db_config = Doozer::Configs.db()
8
+ Doozer::Configs.db_conn = Sequel.connect({
9
+ :adapter => db_config["adapter"],
10
+ :database => db_config["database"],
11
+ :username => db_config["username"],
12
+ :password => db_config["password"],
13
+ :host => db_config["host"]
14
+ })
15
+ printf "ORM: #{Doozer::Configs.orm} initialized...\n"
16
+ end
17
+
18
+ def self.after_request; end
19
+
20
+ end
21
+ end
@@ -0,0 +1,99 @@
1
+ require "erb"
2
+ require "doozer/lib"
3
+ require "doozer/view_helpers"
4
+
5
+ module Doozer
6
+
7
+ # This class facilitates loading and rendering of partials.
8
+ #
9
+ # A partial is an ERB template which starts with an underscore. They behave the same as action view ERB template with the only difference of not having access to Controller instance variables.
10
+ #
11
+ # An example partial: app/views/controller_name/_partial.html.erb
12
+ #
13
+ # By default, the Doozer scaffold creates an app/views/global folder which can be used to place global partials like headers, footers, etc.
14
+ #
15
+ # Partials have access to Doozer::ViewHelpers.
16
+ #
17
+ # All view helpers in app/helpers are automatically included in the Partial class during app initialize.
18
+ #
19
+ # A partial can render another partial and so on and so on.
20
+ class Partial
21
+ attr_accessor :erb, :route
22
+
23
+ include ERB::Util
24
+ include Doozer::Util::Logger
25
+ include Doozer::ViewHelpers
26
+
27
+ # APP_PATH = Dir.pwd
28
+ @@partials={}
29
+
30
+ def initialize(erb, locals, route)
31
+ @erb = erb
32
+ @route = route
33
+ if locals.kind_of? Hash
34
+ locals.each_pair {|key, value|
35
+ #p "#{key}:#{value}"
36
+ self.instance_variable_set("@#{key}".to_sym, value) # :@a, value
37
+ }
38
+ end
39
+ end
40
+
41
+ def bind
42
+ @erb.result(binding)
43
+ end
44
+
45
+ # This class method lazily loads and caches the erb templates of the requested partials
46
+ def self.partial(file=nil, locals={}, route=route)
47
+ #p "Class method: Doozer::Partial#partial"
48
+ if file.index("/").nil?
49
+ name = "#{route.controller}/_#{file}"
50
+ else
51
+ name = "#{file.gsub(/\//,'/_')}"
52
+ end
53
+ load_partial(name) if @@partials[name].nil?
54
+ erb = @@partials[name]
55
+ if erb
56
+ partial = Doozer::Partial.new(erb, locals, route)
57
+ partial.bind()
58
+ else
59
+ printf "--no partial exists for #{file}\n"
60
+ end
61
+ end
62
+
63
+ # Renders and returns a partial template with the given file_name and local variables.
64
+ #
65
+ # * file - expects a string. By default, if you don't pass a controller, it's assumed the lookup location is the current route.controller path in the views folder.
66
+ # You must omit the underscore when passing the file_name.
67
+ # A partial is automatically assumed to be html format. It shouldn't matter if you display an html partial inside a view with a different format.
68
+ #
69
+ # * locals - All local key/values are instantiated as instance variables acessable from the partial template. The controller.request variable is appended to locals and is also accessable as an instance variable from the partial template.
70
+ def partial(file=nil, locals={})
71
+ locals[:request] = @request if not @request.nil?
72
+ Doozer::Partial.partial(file, locals, route=@route)
73
+ end
74
+
75
+ # Load and cache partial ERB template with the given file_name.
76
+ def self.load_partial(name)
77
+ file = File.join(Doozer::Configs.app_path,"app/views/#{name}.html.erb")
78
+ results = []
79
+ begin
80
+ File.new(file, "r").each { |line| results << line }
81
+ # TODO: throw error if doesn't exist
82
+ @@partials[name] = ERB.new(results.join(""))
83
+ rescue
84
+ printf "sorry couldn't load partial #{name} (#{file}) \n"
85
+ end
86
+ end
87
+
88
+ # Class methods for clearing all cached partials. Mainly a dispatcher for the file watcher to pick up new changes without having to restart the appserver in development mode.
89
+ def self.clear_loaded_partials
90
+ @@partials = {}
91
+ end
92
+
93
+ # Class method for including a view helper.
94
+ def self.include_view_helper(helper)
95
+ m = Doozer::Lib.classify(helper)
96
+ include Object.const_get(m)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,2 @@
1
+ PAGINATE_PLUGIN_ROOT = File.join(File.dirname(__FILE__), 'lib')
2
+ require "#{PAGINATE_PLUGIN_ROOT}/paginate"
@@ -0,0 +1,32 @@
1
+ # = Doozer Paginate
2
+ # This code was lifted (i mean...ported) from the WillPaginate gem for ActiveRecord with a few modifications.
3
+ #
4
+ # Not supported by this plugin but in WillPAginate:
5
+ # * Page numbers between 'Previous' and 'Next' links
6
+ # * NamedScopes
7
+ #
8
+ # See http://wiki.github.com/mislav/will_paginate for more details on useage and license.
9
+ require "#{PAGINATE_PLUGIN_ROOT}/paginate/collection"
10
+ module Paginate
11
+ class << self
12
+ def enable_activerecord
13
+ return if ActiveRecord::Base.respond_to? :paginate
14
+ require "#{PAGINATE_PLUGIN_ROOT}/paginate/finder"
15
+ ActiveRecord::Base.send :include, Paginate::Finder
16
+ end
17
+ def enable_view_helpers
18
+ # return if Doozer::Initializer.respond_to? :paginate
19
+ require "#{PAGINATE_PLUGIN_ROOT}/paginate/view_helpers"
20
+ Doozer::Controller.send :include, Paginate::ViewHelpers
21
+ Doozer::Partial.send :include, Paginate::ViewHelpers
22
+ end
23
+ end
24
+ end
25
+
26
+ # Enable ActiveRecord if it's defined
27
+ Paginate.enable_activerecord if defined? ActiveRecord
28
+
29
+ # Load the View Helpers if Doozer::Initializer is loaded
30
+ Doozer::Initializer.before_rackup do | config |
31
+ Paginate.enable_view_helpers
32
+ end if defined? Doozer::Initializer
@@ -0,0 +1,60 @@
1
+ module Paginate
2
+
3
+ class InvalidPage < ArgumentError
4
+ def initialize(page, page_num)
5
+ super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
6
+ end
7
+ end
8
+
9
+ class Collection < Array
10
+ attr_reader :current_page, :per_page, :total_entries, :total_pages
11
+
12
+ def initialize(page, per_page, total = nil)
13
+ @current_page = page.to_i
14
+ raise InvalidPage.new(page, @current_page) if @current_page < 1
15
+ @per_page = per_page.to_i
16
+ raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
17
+
18
+ self.total_entries = total if total
19
+ end
20
+
21
+ def self.create(page, per_page, total = nil)
22
+ pager = new(page, per_page, total)
23
+ yield pager
24
+ pager
25
+ end
26
+
27
+ def out_of_bounds?
28
+ current_page > total_pages
29
+ end
30
+
31
+ def offset
32
+ (current_page - 1) * per_page
33
+ end
34
+
35
+ def previous_page
36
+ current_page > 1 ? (current_page - 1) : nil
37
+ end
38
+
39
+ def next_page
40
+ current_page < total_pages ? (current_page + 1) : nil
41
+ end
42
+
43
+ def total_entries=(number)
44
+ @total_entries = number.to_i
45
+ @total_pages = (@total_entries / per_page.to_f).ceil
46
+ end
47
+
48
+ def replace(array)
49
+ result = super
50
+
51
+ # The collection is shorter then page limit? Rejoice, because
52
+ # then we know that we are on the last page!
53
+ if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
54
+ self.total_entries = offset + length
55
+ end
56
+
57
+ result
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,116 @@
1
+ module Paginate
2
+ module Finder
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ class << base
7
+ # alias_method_chain :method_missing, :paginate
8
+ # # alias_method_chain :find_every, :paginate
9
+ define_method(:per_page) { 30 } unless respond_to?(:per_page)
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ def paginate(*args)
16
+ options = args.pop
17
+ page, per_page, total_entries = paginate_parse_options(options)
18
+
19
+ finder = (options[:finder] || 'find').to_s
20
+
21
+ if finder == 'find'
22
+ # an array of IDs may have been given:
23
+ total_entries ||= (Array === args.first and args.first.size)
24
+ # :all is implicit
25
+ args.unshift(:all) if args.empty?
26
+ end
27
+
28
+ Paginate::Collection.create(page, per_page, total_entries) do |pager|
29
+ count_options = options.except :page, :per_page, :total_entries, :finder
30
+ find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
31
+
32
+ args << find_options
33
+ # @options_from_last_find = nil
34
+ pager.replace(send(finder, *args) { |*a| yield(*a) if block_given? })
35
+
36
+ # magic counting for user convenience:
37
+ pager.total_entries = paginate_count(count_options, args, finder) unless pager.total_entries
38
+ end
39
+
40
+
41
+ end
42
+
43
+ def paginate_parse_options(options)
44
+ raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
45
+ options = options.symbolize_keys
46
+ raise ArgumentError, ':page parameter required' unless options.key? :page
47
+
48
+ if options[:count] and options[:total_entries]
49
+ raise ArgumentError, ':count and :total_entries are mutually exclusive'
50
+ end
51
+
52
+ page = options[:page] || 1
53
+ per_page = options[:per_page] || self.per_page
54
+ total = options[:total_entries]
55
+ [page, per_page, total]
56
+ end
57
+
58
+ # Does the not-so-trivial job of finding out the total number of entries
59
+ # in the database. It relies on the ActiveRecord +count+ method.
60
+ def paginate_count(options, args, finder)
61
+ excludees = [:count, :order, :limit, :offset, :readonly]
62
+ excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
63
+
64
+ # we may be in a model or an association proxy
65
+ klass = (@owner and @reflection) ? @reflection.klass : self
66
+
67
+ # Use :select from scope if it isn't already present.
68
+ options[:select] = scope(:find, :select) unless options[:select]
69
+
70
+ if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
71
+ # Remove quoting and check for table_name.*-like statement.
72
+ if options[:select].gsub('`', '') =~ /\w+\.\*/
73
+ options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
74
+ end
75
+ else
76
+ excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
77
+ end
78
+
79
+ # count expects (almost) the same options as find
80
+ count_options = options.except *excludees
81
+
82
+ # merge the hash found in :count
83
+ # this allows you to specify :select, :order, or anything else just for the count query
84
+ count_options.update options[:count] if options[:count]
85
+
86
+ # forget about includes if they are irrelevant (Rails 2.1)
87
+ # if count_options[:include] and
88
+ # klass.private_methods.include_method?(:references_eager_loaded_tables?) and
89
+ # !klass.send(:references_eager_loaded_tables?, count_options)
90
+ # count_options.delete :include
91
+ # end
92
+
93
+ # we may have to scope ...
94
+ counter = Proc.new { count(count_options) }
95
+
96
+ count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
97
+ # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
98
+ # then execute the count with the scoping provided by the with_finder
99
+ send(scoper, &counter)
100
+ # elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
101
+ # # extract conditions from calls like "paginate_by_foo_and_bar"
102
+ # attribute_names = $2.split('_and_')
103
+ # conditions = construct_attributes_from_arguments(attribute_names, args)
104
+ # with_scope(:find => { :conditions => conditions }, &counter)
105
+ else
106
+ counter.call
107
+ end
108
+
109
+ count.respond_to?(:length) ? count.length : count
110
+ end
111
+
112
+
113
+ end # ClassMethods
114
+
115
+ end
116
+ end
@@ -0,0 +1,37 @@
1
+ module Paginate
2
+ module ViewHelpers
3
+ # default options that can be overridden on the global level
4
+ @@pagination_options = {
5
+ :class => 'pagination',
6
+ :previous_label => '&laquo; Previous',
7
+ :next_label => 'Next &raquo;',
8
+ :inner_window => 4, # links around the current page
9
+ :outer_window => 1, # links around beginning and end
10
+ :separator => ' ', # single space is friendly to spiders and non-graphic browsers
11
+ :param_name => :page,
12
+ :params => {},
13
+ :page_links => true,
14
+ :container => true,
15
+ :debug => false
16
+ }
17
+ mattr_reader :pagination_options
18
+
19
+ def paginate(collection, options={})
20
+ #Collection => :current_page, :per_page, :total_entries, :total_pages
21
+ opt = @@pagination_options
22
+ opt.update(options)
23
+ out=[]
24
+ if opt[:debug]
25
+ out.push("current_page:#{collection.current_page} / ")
26
+ out.push("per_page:#{collection.per_page} / ")
27
+ out.push("total_entries:#{collection.total_entries} / ")
28
+ out.push("total_pages:#{collection.total_pages} <br />")
29
+ end
30
+ out.push("<div class=\"pagination_container\">") if opt[:container]
31
+ out.push(link(opt[:previous_label], {:page=>collection.previous_page}.update(opt[:params]), {:class=>opt[:class]}) ) if collection.previous_page
32
+ out.push(link(opt[:next_label], {:page=>collection.next_page}.update(opt[:params]), {:class=>opt[:class]}) ) if collection.next_page
33
+ out.push("</div>") if opt[:container]
34
+ return out.join(opt[:separator])
35
+ end
36
+ end
37
+ end