bootstrap_pager 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.idea/encodings.xml +5 -0
  4. data/.idea/misc.xml +5 -0
  5. data/.idea/modules.xml +9 -0
  6. data/.idea/pager.iml +50 -0
  7. data/.idea/scopes/scope_settings.xml +5 -0
  8. data/.idea/vcs.xml +7 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +20 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.rdoc +307 -0
  13. data/Rakefile +42 -0
  14. data/app/assets/javascripts/infinitescroll.js +8 -0
  15. data/app/helpers/pager_helper.rb +5 -0
  16. data/app/views/pager/_first_page.html.erb +11 -0
  17. data/app/views/pager/_first_page.html.haml +9 -0
  18. data/app/views/pager/_first_page.html.slim +10 -0
  19. data/app/views/pager/_gap.html.erb +8 -0
  20. data/app/views/pager/_gap.html.haml +8 -0
  21. data/app/views/pager/_gap.html.slim +10 -0
  22. data/app/views/pager/_last_page.html.erb +11 -0
  23. data/app/views/pager/_last_page.html.haml +9 -0
  24. data/app/views/pager/_last_page.html.slim +10 -0
  25. data/app/views/pager/_next_page.html.erb +11 -0
  26. data/app/views/pager/_next_page.html.haml +9 -0
  27. data/app/views/pager/_next_page.html.slim +10 -0
  28. data/app/views/pager/_page.html.erb +14 -0
  29. data/app/views/pager/_page.html.haml +11 -0
  30. data/app/views/pager/_page.html.slim +13 -0
  31. data/app/views/pager/_paginator.html.erb +25 -0
  32. data/app/views/pager/_paginator.html.haml +19 -0
  33. data/app/views/pager/_paginator.html.slim +20 -0
  34. data/app/views/pager/_prev_page.html.erb +11 -0
  35. data/app/views/pager/_prev_page.html.haml +9 -0
  36. data/app/views/pager/_prev_page.html.slim +10 -0
  37. data/bootstrap_pager.gemspec +36 -0
  38. data/config/locales/pager.yml +19 -0
  39. data/gemfiles/active_record_30.gemfile +9 -0
  40. data/gemfiles/active_record_31.gemfile +7 -0
  41. data/gemfiles/active_record_32.gemfile +10 -0
  42. data/gemfiles/active_record_40.gemfile +8 -0
  43. data/gemfiles/active_record_edge.gemfile +11 -0
  44. data/gemfiles/data_mapper_12.gemfile +15 -0
  45. data/gemfiles/mongo_mapper.gemfile +10 -0
  46. data/gemfiles/mongoid_24.gemfile +7 -0
  47. data/gemfiles/mongoid_30.gemfile +12 -0
  48. data/gemfiles/sinatra_13.gemfile +13 -0
  49. data/gemfiles/sinatra_14.gemfile +13 -0
  50. data/lib/bootstrap_pager/config.rb +51 -0
  51. data/lib/bootstrap_pager/grape.rb +4 -0
  52. data/lib/bootstrap_pager/helpers/action_view_extension.rb +152 -0
  53. data/lib/bootstrap_pager/helpers/paginator.rb +186 -0
  54. data/lib/bootstrap_pager/helpers/sinatra_helpers.rb +144 -0
  55. data/lib/bootstrap_pager/helpers/tags.rb +96 -0
  56. data/lib/bootstrap_pager/hooks.rb +41 -0
  57. data/lib/bootstrap_pager/models/active_record_extension.rb +22 -0
  58. data/lib/bootstrap_pager/models/active_record_model_extension.rb +20 -0
  59. data/lib/bootstrap_pager/models/active_record_relation_methods.rb +29 -0
  60. data/lib/bootstrap_pager/models/array_extension.rb +58 -0
  61. data/lib/bootstrap_pager/models/configuration_methods.rb +48 -0
  62. data/lib/bootstrap_pager/models/data_mapper_collection_methods.rb +15 -0
  63. data/lib/bootstrap_pager/models/data_mapper_extension.rb +48 -0
  64. data/lib/bootstrap_pager/models/mongo_mapper_extension.rb +18 -0
  65. data/lib/bootstrap_pager/models/mongoid_criteria_methods.rb +23 -0
  66. data/lib/bootstrap_pager/models/mongoid_extension.rb +33 -0
  67. data/lib/bootstrap_pager/models/page_scope_methods.rb +70 -0
  68. data/lib/bootstrap_pager/models/plucky_criteria_methods.rb +18 -0
  69. data/lib/bootstrap_pager/railtie.rb +7 -0
  70. data/lib/bootstrap_pager/sinatra.rb +5 -0
  71. data/lib/bootstrap_pager/version.rb +3 -0
  72. data/lib/bootstrap_pager.rb +38 -0
  73. data/lib/generators/pager/config_generator.rb +16 -0
  74. data/lib/generators/pager/templates/pager_config.rb +10 -0
  75. data/lib/generators/pager/views_generator.rb +118 -0
  76. data/spec/config/config_spec.rb +91 -0
  77. data/spec/fake_app/active_record/config.rb +3 -0
  78. data/spec/fake_app/active_record/models.rb +57 -0
  79. data/spec/fake_app/data_mapper/config.rb +7 -0
  80. data/spec/fake_app/data_mapper/models.rb +27 -0
  81. data/spec/fake_app/log/development.log +832 -0
  82. data/spec/fake_app/mongo_mapper/config.rb +2 -0
  83. data/spec/fake_app/mongo_mapper/models.rb +9 -0
  84. data/spec/fake_app/mongoid/config.rb +16 -0
  85. data/spec/fake_app/mongoid/models.rb +22 -0
  86. data/spec/fake_app/rails_app.rb +67 -0
  87. data/spec/fake_app/sinatra_app.rb +22 -0
  88. data/spec/fake_gem.rb +4 -0
  89. data/spec/helpers/action_view_extension_spec.rb +292 -0
  90. data/spec/helpers/helpers_spec.rb +135 -0
  91. data/spec/helpers/sinatra_helpers_spec.rb +170 -0
  92. data/spec/helpers/tags_spec.rb +140 -0
  93. data/spec/models/active_record/active_record_relation_methods_spec.rb +47 -0
  94. data/spec/models/active_record/default_per_page_spec.rb +32 -0
  95. data/spec/models/active_record/max_pages_spec.rb +23 -0
  96. data/spec/models/active_record/max_per_page_spec.rb +32 -0
  97. data/spec/models/active_record/scopes_spec.rb +242 -0
  98. data/spec/models/array_spec.rb +150 -0
  99. data/spec/models/data_mapper/data_mapper_spec.rb +207 -0
  100. data/spec/models/mongo_mapper/mongo_mapper_spec.rb +84 -0
  101. data/spec/models/mongoid/mongoid_spec.rb +126 -0
  102. data/spec/requests/users_spec.rb +53 -0
  103. data/spec/spec_helper.rb +33 -0
  104. data/spec/spec_helper_for_sinatra.rb +34 -0
  105. data/spec/support/database_cleaner.rb +16 -0
  106. data/spec/support/matchers.rb +52 -0
  107. data/vendor/assets/javascripts/jquery.infinitescroll.js +814 -0
  108. data/vendor/assets/javascripts/jquery.infinitescroll.min.js +1 -0
  109. metadata +311 -0
@@ -0,0 +1,48 @@
1
+ require 'bootstrap_pager/models/data_mapper_collection_methods'
2
+
3
+ module BootstrapPager
4
+ module DataMapperExtension
5
+ module Paginatable
6
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
7
+ def #{BootstrapPager.config.page_method_name}(num = 1)
8
+ num = [num.to_i, 1].max - 1
9
+ all(:limit => default_per_page, :offset => default_per_page * num).extend Paginating
10
+ end
11
+ RUBY
12
+ end
13
+
14
+ module Paginating
15
+ include BootstrapPager::PageScopeMethods
16
+
17
+ def all(options={})
18
+ super.extend Paginating
19
+ end
20
+
21
+ def per(num)
22
+ super.extend Paginating
23
+ end
24
+ end
25
+
26
+ module Collection
27
+ extend ActiveSupport::Concern
28
+ included do
29
+ include BootstrapPager::ConfigurationMethods::ClassMethods
30
+ include BootstrapPager::DataMapperCollectionMethods
31
+ include Paginatable
32
+ end
33
+ end
34
+
35
+ module Model
36
+ include BootstrapPager::ConfigurationMethods::ClassMethods
37
+ include Paginatable
38
+
39
+ def limit(val)
40
+ all(:limit => val)
41
+ end
42
+
43
+ def offset(val)
44
+ all(:offset => val)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ require 'bootstrap_pager/models/plucky_criteria_methods'
2
+
3
+ module BootstrapPager
4
+ module MongoMapperExtension
5
+ module Document
6
+ extend ActiveSupport::Concern
7
+ include BootstrapPager::ConfigurationMethods
8
+
9
+ included do
10
+ # Fetch the values at the specified page number
11
+ # Model.page(5)
12
+ scope BootstrapPager.config.page_method_name, Proc.new {|num|
13
+ limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ module BootstrapPager
2
+ module MongoidCriteriaMethods
3
+ def limit_value #:nodoc:
4
+ options[:limit]
5
+ end
6
+
7
+ def offset_value #:nodoc:
8
+ options[:skip]
9
+ end
10
+
11
+ def total_count #:nodoc:
12
+ embedded? ? unpage.count : count
13
+ end
14
+
15
+ private
16
+ def unpage
17
+ clone.tap do |crit|
18
+ crit.options.delete :limit
19
+ crit.options.delete :skip
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'bootstrap_pager/models/mongoid_criteria_methods'
2
+
3
+ module BootstrapPager
4
+ module MongoidExtension
5
+ module Criteria
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
10
+ def #{BootstrapPager.config.page_method_name}(*args)
11
+ super(*args).criteria.merge(self)
12
+ end
13
+ RUBY
14
+ end
15
+ end
16
+
17
+ module Document
18
+ extend ActiveSupport::Concern
19
+ include BootstrapPager::ConfigurationMethods
20
+
21
+ included do
22
+ # Fetch the values at the specified page number
23
+ # Model.page(5)
24
+ scope BootstrapPager.config.page_method_name, Proc.new {|num|
25
+ limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
26
+ } do
27
+ include BootstrapPager::MongoidCriteriaMethods
28
+ include BootstrapPager::PageScopeMethods
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,70 @@
1
+ module BootstrapPager
2
+ module PageScopeMethods
3
+ # Specify the <tt>per_page</tt> value for the preceding <tt>page</tt> scope
4
+ # Model.page(3).per(10)
5
+ def per(num)
6
+ if (n = num.to_i) <= 0
7
+ self
8
+ elsif max_per_page && max_per_page < n
9
+ limit(max_per_page).offset(offset_value / limit_value * max_per_page)
10
+ else
11
+ limit(n).offset(offset_value / limit_value * n)
12
+ end
13
+ end
14
+
15
+ def padding(num)
16
+ @_padding = num
17
+ offset(offset_value + num.to_i)
18
+ end
19
+
20
+ # Total number of pages
21
+ def total_pages
22
+ count_without_padding = total_count
23
+ count_without_padding -= @_padding if defined?(@_padding) && @_padding
24
+ count_without_padding = 0 if count_without_padding < 0
25
+
26
+ total_pages_count = (count_without_padding.to_f / limit_value).ceil
27
+ if max_pages.present? && max_pages < total_pages_count
28
+ max_pages
29
+ else
30
+ total_pages_count
31
+ end
32
+ end
33
+ #FIXME for compatibility. remove num_pages at some time in the future
34
+ alias num_pages total_pages
35
+
36
+ # Current page number
37
+ def current_page
38
+ offset_without_padding = offset_value
39
+ offset_without_padding -= @_padding if defined?(@_padding) && @_padding
40
+ offset_without_padding = 0 if offset_without_padding < 0
41
+
42
+ (offset_without_padding / limit_value) + 1
43
+ end
44
+
45
+ # Next page number in the collection
46
+ def next_page
47
+ current_page + 1 unless last_page?
48
+ end
49
+
50
+ # Previous page number in the collection
51
+ def prev_page
52
+ current_page - 1 unless first_page?
53
+ end
54
+
55
+ # First page of the collection?
56
+ def first_page?
57
+ current_page == 1
58
+ end
59
+
60
+ # Last page of the collection?
61
+ def last_page?
62
+ current_page >= total_pages
63
+ end
64
+
65
+ # Out of range of the collection?
66
+ def out_of_range?
67
+ current_page > total_pages
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,18 @@
1
+ module BootstrapPager
2
+ module PluckyCriteriaMethods
3
+ include BootstrapPager::PageScopeMethods
4
+ include BootstrapPager::ConfigurationMethods::ClassMethods
5
+
6
+ def limit_value #:nodoc:
7
+ options[:limit]
8
+ end
9
+
10
+ def offset_value #:nodoc:
11
+ options[:skip]
12
+ end
13
+
14
+ def total_count #:nodoc:
15
+ count
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module BootstrapPager
2
+ class Railtie < ::Rails::Railtie #:nodoc:
3
+ initializer 'bootstrap_pager' do |_app|
4
+ BootstrapPager::Hooks.init
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ require 'sinatra/base'
2
+ require 'bootstrap_pager'
3
+ require 'bootstrap_pager/helpers/sinatra_helpers'
4
+
5
+ BootstrapPager::Hooks.init
@@ -0,0 +1,3 @@
1
+ module BootstrapPager
2
+ VERSION = '0.9.0'
3
+ end
@@ -0,0 +1,38 @@
1
+ module BootstrapPager
2
+ end
3
+
4
+ # load Rails/Railtie
5
+ begin
6
+ require 'rails'
7
+ rescue LoadError
8
+ #do nothing
9
+ end
10
+
11
+ $stderr.puts <<-EOC if !defined?(Rails) && !defined?(Sinatra) && !defined?(Grape)
12
+ warning: no framework detected.
13
+
14
+ Your Gemfile might not be configured properly.
15
+ ---- e.g. ----
16
+ Rails:
17
+ gem 'bootstrap_pager'
18
+
19
+ Sinatra/Padrino:
20
+ gem 'bootstrap_pager', :require => 'bootstrap_pager/sinatra'
21
+
22
+ Grape:
23
+ gem 'bootstrap_pager', :require => 'bootstrap_pager/grape'
24
+
25
+ EOC
26
+
27
+ # load BootstrapPager components
28
+ require 'bootstrap_pager/config'
29
+ require 'bootstrap_pager/helpers/paginator'
30
+ require 'bootstrap_pager/models/page_scope_methods'
31
+ require 'bootstrap_pager/models/configuration_methods'
32
+ require 'bootstrap_pager/hooks'
33
+
34
+ # if not using Railtie, call `BootstrapPager::Hooks.init` directly
35
+ if defined? Rails
36
+ require 'bootstrap_pager/railtie'
37
+ require 'bootstrap_pager/engine'
38
+ end
@@ -0,0 +1,16 @@
1
+ module BootstrapPager
2
+ module Generators
3
+ class ConfigGenerator < Rails::Generators::Base
4
+ source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
5
+
6
+ desc <<DESC
7
+ Description:
8
+ Copies BootstrapPager configuration file to your application's initializer directory.
9
+ DESC
10
+
11
+ def copy_config_file
12
+ template 'bootstrap_pager_config.rb', 'config/initializers/bootstrap_pager_config.rb'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ BootstrapPager.configure do |config|
2
+ # config.default_per_page = 25
3
+ # config.max_per_page = nil
4
+ # config.window = 4
5
+ # config.outer_window = 0
6
+ # config.left = 0
7
+ # config.right = 0
8
+ # config.page_method_name = :page
9
+ # config.param_name = :page
10
+ end
@@ -0,0 +1,118 @@
1
+ module BootstrapPager
2
+ module Generators
3
+
4
+ class ViewsGenerator < Rails::Generators::NamedBase
5
+ source_root File.expand_path('../../../../app/views/bootstrap_pager', __FILE__)
6
+
7
+ class_option :template_engine, :type => :string, :aliases => '-e', :desc => 'Template engine for the views. Available options are "erb", "haml", and "slim".'
8
+
9
+ def self.banner #:nodoc:
10
+ <<-BANNER.chomp
11
+ rails g bootstrap_pager:views THEME [options]
12
+
13
+ Copies all paginator partial templates to your application.
14
+ You can choose a template THEME by specifying one from the list below:
15
+
16
+ - default
17
+ The default one.
18
+ This one is used internally while you don't override the partials.
19
+ #{themes.map {|t| " - #{t.name}\n#{t.description}"}.join("\n")}
20
+ BANNER
21
+ end
22
+
23
+ desc ''
24
+ def copy_or_fetch #:nodoc:
25
+ return copy_default_views if file_name == 'default'
26
+
27
+ themes = self.class.themes
28
+ if theme = themes.detect {|t| t.name == file_name}
29
+ download_templates theme
30
+ else
31
+ say %Q[no such theme: #{file_name}\n avaliable themes: #{themes.map(&:name).join ", "}]
32
+ end
33
+ end
34
+
35
+ private
36
+ def self.themes
37
+ begin
38
+ @themes ||= GitHubApiHelper.get_files_in_master.group_by {|fn, _| fn[0...(fn.index('/') || 0)]}.delete_if {|fn, _| fn.blank?}.map do |name, files|
39
+ Theme.new name, files
40
+ end
41
+ rescue SocketError
42
+ []
43
+ end
44
+ end
45
+
46
+ def download_templates(theme)
47
+ theme.templates_for(template_engine).each do |template|
48
+ say " downloading #{template.name} from bootstrap_pager_themes..."
49
+ create_file template.name, GitHubApiHelper.get_content_for("#{theme.name}/#{template.name}")
50
+ end
51
+ end
52
+
53
+ def copy_default_views
54
+ filename_pattern = File.join self.class.source_root, "*.html.#{template_engine}"
55
+ Dir.glob(filename_pattern).map {|f| File.basename f}.each do |f|
56
+ copy_file f, "app/views/bootstrap_pager/#{f}"
57
+ end
58
+ end
59
+
60
+ def template_engine
61
+ options[:template_engine].try(:to_s).try(:downcase) || 'erb'
62
+ end
63
+ end
64
+
65
+ Template = Struct.new(:name, :sha) do
66
+ def description?
67
+ name == 'DESCRIPTION'
68
+ end
69
+
70
+ def view?
71
+ name =~ /^app\/views\//
72
+ end
73
+
74
+ def engine #:nodoc:
75
+ File.extname(name).sub /^\./, ''
76
+ end
77
+ end
78
+
79
+ class Theme
80
+ attr_accessor :name
81
+ def initialize(name, templates) #:nodoc:
82
+ @name, @templates = name, templates.map {|fn, sha| Template.new fn.sub(/^#{name}\//, ''), sha}
83
+ end
84
+
85
+ def description #:nodoc:
86
+ file = @templates.detect(&:description?)
87
+ return "#{' ' * 12}#{name}" unless file
88
+ GitHubApiHelper.get_content_for("#{@name}/#{file.name}").chomp.gsub(/^/, ' ' * 12)
89
+ end
90
+
91
+ def templates_for(template_engine) #:nodoc:
92
+ @templates.select {|t| !t.description?}.select {|t| !t.view? || (t.engine == template_engine)}
93
+ end
94
+ end
95
+
96
+ module GitHubApiHelper
97
+ def get_files_in_master
98
+ master_tree_sha = open('https://api.github.com/repos/amatsuda/bootstrap_pager_themes/git/refs/heads/master') do |json|
99
+ ActiveSupport::JSON.decode(json)['object']['sha']
100
+ end
101
+ open('https://api.github.com/repos/amatsuda/bootstrap_pager_themes/git/trees/' + master_tree_sha + '?recursive=1') do |json|
102
+ blobs = ActiveSupport::JSON.decode(json)['tree'].find_all {|i| i['type'] == 'blob' }
103
+ blobs.map do |blob|
104
+ [blob['path'], blob['sha']]
105
+ end
106
+ end
107
+ end
108
+ module_function :get_files_in_master
109
+
110
+ def get_content_for(path)
111
+ open('https://api.github.com/repos/amatsuda/bootstrap_pager_themes/contents/' + path) do |json|
112
+ Base64.decode64(ActiveSupport::JSON.decode(json)['content'])
113
+ end
114
+ end
115
+ module_function :get_content_for
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe BootstrapPager::Configuration do
4
+ subject { BootstrapPager.config }
5
+ describe 'default_per_page' do
6
+ context 'by default' do
7
+ its(:default_per_page) { should == 25 }
8
+ end
9
+ context 'configured via config block' do
10
+ before do
11
+ BootstrapPager.configure {|c| c.default_per_page = 17}
12
+ end
13
+ its(:default_per_page) { should == 17 }
14
+ after do
15
+ BootstrapPager.configure {|c| c.default_per_page = 25}
16
+ end
17
+ end
18
+ end
19
+
20
+ describe 'max_per_page' do
21
+ context 'by default' do
22
+ its(:max_per_page) { should == nil }
23
+ end
24
+ context 'configure via config block' do
25
+ before do
26
+ BootstrapPager.configure {|c| c.max_per_page = 100}
27
+ end
28
+ its(:max_per_page) { should == 100 }
29
+ after do
30
+ BootstrapPager.configure {|c| c.max_per_page = nil}
31
+ end
32
+ end
33
+ end
34
+
35
+ describe 'window' do
36
+ context 'by default' do
37
+ its(:window) { should == 4 }
38
+ end
39
+ end
40
+
41
+ describe 'outer_window' do
42
+ context 'by default' do
43
+ its(:outer_window) { should == 0 }
44
+ end
45
+ end
46
+
47
+ describe 'left' do
48
+ context 'by default' do
49
+ its(:left) { should == 0 }
50
+ end
51
+ end
52
+
53
+ describe 'right' do
54
+ context 'by default' do
55
+ its(:right) { should == 0 }
56
+ end
57
+ end
58
+
59
+ describe 'param_name' do
60
+ context 'by default' do
61
+ its(:param_name) { should == :page }
62
+ end
63
+
64
+ context 'configured via config block' do
65
+ before do
66
+ BootstrapPager.configure {|c| c.param_name = lambda { :test } }
67
+ end
68
+
69
+ its(:param_name) { should == :test }
70
+
71
+ after do
72
+ BootstrapPager.configure {|c| c.param_name = :page }
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'max_pages' do
78
+ context 'by default' do
79
+ its(:max_pages) { should == nil }
80
+ end
81
+ context 'configure via config block' do
82
+ before do
83
+ BootstrapPager.configure {|c| c.max_pages = 5}
84
+ end
85
+ its(:max_pages) { should == 5 }
86
+ after do
87
+ BootstrapPager.configure {|c| c.max_pages = nil}
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ # database
2
+ ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
3
+ ActiveRecord::Base.establish_connection('test')
@@ -0,0 +1,57 @@
1
+ # models
2
+ class User < ActiveRecord::Base
3
+ has_many :authorships
4
+ has_many :readerships
5
+ has_many :books_authored, :through => :authorships, :source => :book
6
+ has_many :books_read, :through => :readerships, :source => :book
7
+ has_many :addresses, :class_name => 'User::Address'
8
+
9
+ def readers
10
+ User.joins(:books_read => :authors).where(:authors_books => {:id => self})
11
+ end
12
+
13
+ scope :by_name, order(:name)
14
+ scope :by_read_count, lambda {
15
+ cols = if connection.adapter_name == "PostgreSQL"
16
+ column_names.map { |column| %{"users"."#{column}"} }.join(", ")
17
+ else
18
+ '"users"."id"'
19
+ end
20
+ group(cols).select("count(readerships.id) AS read_count, #{cols}").order('read_count DESC')
21
+ }
22
+ end
23
+ class Authorship < ActiveRecord::Base
24
+ belongs_to :user
25
+ belongs_to :book
26
+ end
27
+ class Readership < ActiveRecord::Base
28
+ belongs_to :user
29
+ belongs_to :book
30
+ end
31
+ class Book < ActiveRecord::Base
32
+ has_many :authorships
33
+ has_many :readerships
34
+ has_many :authors, :through => :authorships, :source => :user
35
+ has_many :readers, :through => :readerships, :source => :user
36
+ end
37
+ # a model that is a descendant of AR::Base but doesn't directly inherit AR::Base
38
+ class Admin < User
39
+ end
40
+ # a model with namespace
41
+ class User::Address < ActiveRecord::Base
42
+ belongs_to :user
43
+ end
44
+
45
+ #migrations
46
+ class CreateAllTables < ActiveRecord::Migration
47
+ def self.up
48
+ create_table(:gem_defined_models) { |t| t.string :name; t.integer :age }
49
+ create_table(:users) {|t| t.string :name; t.integer :age}
50
+ create_table(:books) {|t| t.string :title}
51
+ create_table(:readerships) {|t| t.integer :user_id; t.integer :book_id }
52
+ create_table(:authorships) {|t| t.integer :user_id; t.integer :book_id }
53
+ create_table(:user_addresses) {|t| t.string :street; t.integer :user_id }
54
+ end
55
+ end
56
+ ActiveRecord::Migration.verbose = false
57
+ CreateAllTables.up
@@ -0,0 +1,7 @@
1
+ DataMapper.setup(:default, 'sqlite::memory:')
2
+
3
+ class DataMapper::Collection
4
+ def default_per_page
5
+ model.default_per_page
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ class User
2
+ include ::DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :name, String, :required => true
6
+ property :age, Integer
7
+
8
+ has n, :projects, :through => Resource
9
+ end
10
+
11
+ class User::Address
12
+ include ::DataMapper::Resource
13
+
14
+ property :id, Serial
15
+ end
16
+
17
+ class Project
18
+ include ::DataMapper::Resource
19
+
20
+ property :id, Serial
21
+ property :name, String, :required => true
22
+
23
+ has n, :users, :through => Resource
24
+ end
25
+
26
+ DataMapper.finalize
27
+ DataMapper.auto_migrate!