ants 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ce5b471679d1171433e6413906b18f29db61f66
4
+ data.tar.gz: a3e3981563e6ed41ef7805928d8146ff90cde0c4
5
+ SHA512:
6
+ metadata.gz: e4ffc38efd70ed6c060f324463bf20d98201df87d57e553a4a9cc0e26f7f4f0499705c2d1ce2bf70ca443bfff031644dee70e2302acdc2ed7d77e5055130d077
7
+ data.tar.gz: 7b01d8e62ed8e5fdff08303efae9b175c8f0173165d12ab6f634bc97b088b5b3d52a79504e0fc13c8d298d2a63aa84a6879e2c356a95b54181303199237e241b
data/.gitignore ADDED
@@ -0,0 +1,38 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /log
5
+ /tmp
6
+ /db/*.sqlite3
7
+ /db/*.sqlite3-journal
8
+ /public/system
9
+ /coverage/
10
+ /spec/tmp
11
+ **.orig
12
+ rerun.txt
13
+ pickle-email-*.html
14
+
15
+ # TODO Comment out these rules if you are OK with secrets being uploaded to the repo
16
+ config/initializers/secret_token.rb
17
+ config/secrets.yml
18
+
19
+ ## Environment normalisation:
20
+ /.bundle
21
+ /vendor/bundle
22
+
23
+ # these should all be checked in to normalise the environment:
24
+ # Gemfile.lock, .ruby-version, .ruby-gemset
25
+
26
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
27
+ .rvmrc
28
+
29
+ # if using bower-rails ignore default bower_components path bower.json files
30
+ /vendor/assets/bower_components
31
+ *.bowerrc
32
+ bower.json
33
+
34
+ # Ignore pow environment settings
35
+ .powenv
36
+
37
+ # Mac chached files
38
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,141 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ants (0.1.0)
5
+ mongoid (>= 4.0)
6
+ mongoid-slug (>= 4.0.0)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ actionmailer (4.2.1)
12
+ actionpack (= 4.2.1)
13
+ actionview (= 4.2.1)
14
+ activejob (= 4.2.1)
15
+ mail (~> 2.5, >= 2.5.4)
16
+ rails-dom-testing (~> 1.0, >= 1.0.5)
17
+ actionpack (4.2.1)
18
+ actionview (= 4.2.1)
19
+ activesupport (= 4.2.1)
20
+ rack (~> 1.6)
21
+ rack-test (~> 0.6.2)
22
+ rails-dom-testing (~> 1.0, >= 1.0.5)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.1)
24
+ actionview (4.2.1)
25
+ activesupport (= 4.2.1)
26
+ builder (~> 3.1)
27
+ erubis (~> 2.7.0)
28
+ rails-dom-testing (~> 1.0, >= 1.0.5)
29
+ rails-html-sanitizer (~> 1.0, >= 1.0.1)
30
+ activejob (4.2.1)
31
+ activesupport (= 4.2.1)
32
+ globalid (>= 0.3.0)
33
+ activemodel (4.2.1)
34
+ activesupport (= 4.2.1)
35
+ builder (~> 3.1)
36
+ activerecord (4.2.1)
37
+ activemodel (= 4.2.1)
38
+ activesupport (= 4.2.1)
39
+ arel (~> 6.0)
40
+ activesupport (4.2.1)
41
+ i18n (~> 0.7)
42
+ json (~> 1.7, >= 1.7.7)
43
+ minitest (~> 5.1)
44
+ thread_safe (~> 0.3, >= 0.3.4)
45
+ tzinfo (~> 1.1)
46
+ arel (6.0.0)
47
+ bson (2.3.0)
48
+ builder (3.2.2)
49
+ connection_pool (2.2.0)
50
+ diff-lcs (1.2.5)
51
+ erubis (2.7.0)
52
+ globalid (0.3.5)
53
+ activesupport (>= 4.1.0)
54
+ i18n (0.7.0)
55
+ json (1.8.2)
56
+ loofah (2.0.1)
57
+ nokogiri (>= 1.5.9)
58
+ mail (2.6.3)
59
+ mime-types (>= 1.16, < 3)
60
+ mime-types (2.5)
61
+ mini_portile (0.6.2)
62
+ minitest (5.6.1)
63
+ mongoid (4.0.2)
64
+ activemodel (~> 4.0)
65
+ moped (~> 2.0.0)
66
+ origin (~> 2.1)
67
+ tzinfo (>= 0.3.37)
68
+ mongoid-slug (4.0.0)
69
+ mongoid (>= 3.0)
70
+ stringex (~> 2.0)
71
+ moped (2.0.4)
72
+ bson (~> 2.2)
73
+ connection_pool (~> 2.0)
74
+ optionable (~> 0.2.0)
75
+ nokogiri (1.6.6.2)
76
+ mini_portile (~> 0.6.0)
77
+ optionable (0.2.0)
78
+ origin (2.1.1)
79
+ rack (1.6.0)
80
+ rack-test (0.6.3)
81
+ rack (>= 1.0)
82
+ rails (4.2.1)
83
+ actionmailer (= 4.2.1)
84
+ actionpack (= 4.2.1)
85
+ actionview (= 4.2.1)
86
+ activejob (= 4.2.1)
87
+ activemodel (= 4.2.1)
88
+ activerecord (= 4.2.1)
89
+ activesupport (= 4.2.1)
90
+ bundler (>= 1.3.0, < 2.0)
91
+ railties (= 4.2.1)
92
+ sprockets-rails
93
+ rails-deprecated_sanitizer (1.0.3)
94
+ activesupport (>= 4.2.0.alpha)
95
+ rails-dom-testing (1.0.6)
96
+ activesupport (>= 4.2.0.beta, < 5.0)
97
+ nokogiri (~> 1.6.0)
98
+ rails-deprecated_sanitizer (>= 1.0.1)
99
+ rails-html-sanitizer (1.0.2)
100
+ loofah (~> 2.0)
101
+ railties (4.2.1)
102
+ actionpack (= 4.2.1)
103
+ activesupport (= 4.2.1)
104
+ rake (>= 0.8.7)
105
+ thor (>= 0.18.1, < 2.0)
106
+ rake (10.4.2)
107
+ rspec (3.2.0)
108
+ rspec-core (~> 3.2.0)
109
+ rspec-expectations (~> 3.2.0)
110
+ rspec-mocks (~> 3.2.0)
111
+ rspec-aspic (0.0.2)
112
+ rspec-core (3.2.3)
113
+ rspec-support (~> 3.2.0)
114
+ rspec-expectations (3.2.1)
115
+ diff-lcs (>= 1.2.0, < 2.0)
116
+ rspec-support (~> 3.2.0)
117
+ rspec-mocks (3.2.1)
118
+ diff-lcs (>= 1.2.0, < 2.0)
119
+ rspec-support (~> 3.2.0)
120
+ rspec-support (3.2.2)
121
+ sprockets (3.0.2)
122
+ rack (~> 1.0)
123
+ sprockets-rails (2.2.4)
124
+ actionpack (>= 3.0)
125
+ activesupport (>= 3.0)
126
+ sprockets (>= 2.8, < 4.0)
127
+ stringex (2.5.2)
128
+ thor (0.19.1)
129
+ thread_safe (0.3.5)
130
+ tzinfo (1.2.2)
131
+ thread_safe (~> 0.1)
132
+
133
+ PLATFORMS
134
+ ruby
135
+
136
+ DEPENDENCIES
137
+ ants!
138
+ rails (>= 4.1)
139
+ rake (>= 0.8.7)
140
+ rspec (>= 2.0)
141
+ rspec-aspic (>= 0.0.2)
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 [Slate Studio, LLC.](http://slatestudio.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # ants
2
+ Collection of concerns and helpers for Rails + Mongoid + Character web development
3
+
4
+
5
+ ### Sorted Relations
6
+
7
+ In Mongoid, the HM & HABTM relations return docs in the wrong order. This workaround provides an ability to retrieve related documents in the same order it was stored.
8
+
9
+ Usage example:
10
+
11
+ ```ruby
12
+ class Post
13
+ include Mongoid::Document
14
+ include Ants::SortedRelations
15
+
16
+ field :title
17
+
18
+ has_and_belongs_to_many :authors
19
+ sorted_relations_for :authors
20
+ end
21
+
22
+ post = Post.new title: 'RESTful actions with Mongosteen'
23
+ post.sorted_author_ids = [ Author.create(name: "Oleksandr Kravets").id,
24
+ Author.create(name: "Roman Brazhnyk" ).id,
25
+ Author.create(name: "Maxim Melnyk" ).id ]
26
+
27
+ post.sorted_authors.map(&:name)
28
+ #=> ['Alexander Kravets', 'Roman Brazhnyk', 'Maxim Melnyk']
29
+ ```
30
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ require "rubygems"
3
+ require "bundler"
data/ants.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'ants/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ants'
7
+ s.version = Ants::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = [ 'Alexander Kravets' ]
10
+ s.email = 'alex@slatestudio.com'
11
+ s.license = 'MIT'
12
+ s.homepage = 'https://github.com/slate-studio/ants'
13
+ s.summary = 'Collection of concerns for Rails + Mongoid + Character web development'
14
+ s.description = <<-DESC
15
+ Collection of concerns and helpers for Rails + Mongoid + Character web development
16
+ DESC
17
+
18
+ s.rubyforge_project = 'ants'
19
+ s.files = `git ls-files`.split("\n")
20
+ s.require_paths = [ 'lib' ]
21
+
22
+ s.add_dependency('mongoid', '>= 4.0' )
23
+ s.add_dependency('mongoid-slug', '>= 4.0.0')
24
+
25
+ s.add_development_dependency('rails', [ '>= 4.1' ])
26
+ s.add_development_dependency('rake', [ '>= 0.8.7' ])
27
+ s.add_development_dependency('rspec', [ '>= 2.0' ])
28
+ s.add_development_dependency('rspec-aspic', [ '>= 0.0.2' ])
29
+ end
30
+
31
+
32
+
33
+
data/lib/ants.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'mongoid'
2
+ require 'mongoid_slug'
3
+
4
+ module Ants
5
+ require 'ants/engine'
6
+
7
+ require "mongoid/fake_criteria"
8
+
9
+ require "concerns/ants/id"
10
+ require "concerns/ants/featurable"
11
+ require "concerns/ants/hideable"
12
+ require "concerns/ants/orderable"
13
+ require "concerns/ants/meta"
14
+ require "concerns/ants/slug"
15
+ require "concerns/ants/publication"
16
+ require "concerns/ants/sorted_relations"
17
+ end
@@ -0,0 +1,5 @@
1
+ module Ants
2
+ class Engine < ::Rails::Engine
3
+ # auto wire
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Ants
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,43 @@
1
+ module Ants
2
+ module Featurable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # attributes
7
+ field :featured, type: Boolean, default: false
8
+
9
+ # scopes
10
+ scope :featured, -> { where(featured: true) }
11
+ scope :not_featured, -> { where(featured: false) }
12
+
13
+ # indexes
14
+ index({ featured: 1 })
15
+
16
+
17
+ # helpers
18
+ def featured?
19
+ self.featured
20
+ end
21
+
22
+
23
+ def set_featured!
24
+ return if self.featured?
25
+ self.featured = true
26
+ self.save!
27
+ end
28
+
29
+
30
+ def unset_featured!
31
+ return unless self.featured?
32
+ self.featured = false
33
+ self.save!
34
+ end
35
+
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
43
+
@@ -0,0 +1,43 @@
1
+ module Ants
2
+ module Hideable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ # attributes
7
+ field :hidden, type: Boolean, default: false
8
+
9
+ # scopes
10
+ scope :hidden, -> { where(hidden: true) }
11
+ scope :not_hidden, -> { where(hidden: false) }
12
+
13
+ # indexes
14
+ index({ hidden: 1 })
15
+
16
+
17
+ # helpers
18
+ def hidden?
19
+ self.hidden
20
+ end
21
+
22
+
23
+ def hide!
24
+ return if self.hidden?
25
+ self.hidden = true
26
+ self.save!
27
+ end
28
+
29
+
30
+ def unhide!
31
+ return unless self.hidden?
32
+ self.hidden = false
33
+ self.save!
34
+ end
35
+
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+
42
+
43
+
@@ -0,0 +1,25 @@
1
+ module Ants
2
+ module Id
3
+ extend ActiveSupport::Concern
4
+
5
+ def serializable_hash(options={})
6
+
7
+ attrs = super(options)
8
+
9
+ attrs.each_pair do |k, v|
10
+
11
+ if k.end_with?('_id')
12
+ attrs[k] = v.to_s
13
+ end
14
+
15
+ if k.end_with?('_ids')
16
+ attrs[k] = attrs[k].collect { |id| id.to_s }
17
+ end
18
+
19
+ end
20
+
21
+ return attrs
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ module Ants
2
+ module Meta
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+
7
+ field :_meta_title, default: ''
8
+ field :_meta_description, default: ''
9
+ field :_meta_keywords, default: ''
10
+ field :_opengraph_image_url, default: ''
11
+
12
+
13
+ # these helpers to be used for meta_tags helper,
14
+ # override them to add custom logic
15
+ def meta_title
16
+ _meta_title
17
+ end
18
+
19
+
20
+ def meta_description
21
+ _meta_description
22
+ end
23
+
24
+
25
+ def meta_keywords
26
+ _meta_keywords
27
+ end
28
+
29
+
30
+ def opengraph_image_url
31
+ _opengraph_image_url
32
+ end
33
+
34
+
35
+ end
36
+ end
37
+ end
38
+
39
+
@@ -0,0 +1,33 @@
1
+ # ==========================================================================
2
+ #
3
+ # IMPORTANT: this default value raises "stack level too deep" if concern is
4
+ # included for existing objects, to fix issue update _position
5
+ # value for all objects like this:
6
+ #
7
+ # > ModelClass.all.set(_position: 1000)
8
+ #
9
+ # ==========================================================================
10
+
11
+ module Ants
12
+ module Orderable
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+
17
+ # attributes
18
+ field :_position, type: Float, default: -> { (self.class.all.last.try(:_position) || 1000) + 10 }
19
+
20
+ # scopes
21
+ default_scope -> { order_by(_position: :asc) }
22
+
23
+ # helpers
24
+ index({ _position: 1 })
25
+
26
+
27
+ end
28
+ end
29
+ end
30
+
31
+
32
+
33
+
@@ -0,0 +1,79 @@
1
+ module Ants
2
+ module Publication
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Mongoid::Timestamps
7
+
8
+ include Ants::Id
9
+ include Ants::Featurable
10
+ include Ants::Orderable
11
+ include Ants::Slug
12
+ include Ants::Meta
13
+
14
+ # attributes
15
+ field :title
16
+ field :draft, type: Boolean, default: false
17
+ field :published_at, type: DateTime, default: -> { DateTime.now }
18
+
19
+ # slug
20
+ slug :title
21
+
22
+ # validations
23
+ validates_presence_of :title, :published_at
24
+
25
+ # scopes
26
+ scope :drafts, -> { where(draft: true) }
27
+ scope :ready_to_publish, -> { where(draft: false) }
28
+ scope :scheduled, -> { ready_to_publish.where(:published_at.gt => Time.zone.now) }
29
+ scope :published, -> { ready_to_publish.where(:published_at.lte => Time.zone.now) }
30
+ # override featured scope set by Ants::Featured to add ordering by _position
31
+ scope :featured, -> { unscoped.published.where(featured: true).asc(:_position) }
32
+ scope :featured_first, -> { unscoped.desc(:featured).asc(:_position).desc(:published_at) }
33
+ # override default_scope set by Ants::Orderable
34
+ default_scope -> { desc(:published_at).desc(:created_at) }
35
+
36
+ # indexes @TODO: verify if these indexes are required
37
+ index({ featured: -1, _position: 1, published_at: -1 }) # featured first
38
+ index({ featured: -1, _position: 1 }) # featured
39
+ index({ published_at: -1, created_at: -1 }) # default
40
+
41
+
42
+ # helpers
43
+ def draft?
44
+ self.draft
45
+ end
46
+
47
+
48
+ def published?
49
+ ! draft? && published_at <= Time.zone.now
50
+ end
51
+
52
+
53
+ def scheduled?
54
+ ! draft? && published_at > Time.zone.now
55
+ end
56
+
57
+
58
+ def _list_item_title
59
+ title
60
+ end
61
+
62
+
63
+ def _list_item_subtitle
64
+ published_at.strftime("%B %d, %Y @ %l:%M %P")
65
+ end
66
+
67
+
68
+ def meta_title
69
+ _meta_title.present || title
70
+ end
71
+
72
+
73
+ end
74
+ end
75
+ end
76
+
77
+
78
+
79
+
@@ -0,0 +1,67 @@
1
+ # =============================================================================
2
+ #
3
+ # Ants::Slug provides a flexible way to define a custom slug on a top of
4
+ # Mongoid::Slug module, a few cases are covered:
5
+ #
6
+ # 1. if _slug is not defined, slug is autogenerated based on specified
7
+ # configuration
8
+ # 2. if _slug is defined it's used as default
9
+ # 3. if _slug is empty slug is autogenerated as in step 1
10
+ # 4. if slug configuration field has changed value and _slug is set, _slug
11
+ # is used as default
12
+ #
13
+ # -----------------------------------------------------------------------------
14
+
15
+ module Ants
16
+ module Slug
17
+ extend ActiveSupport::Concern
18
+
19
+
20
+ included do
21
+ include Mongoid::Slug
22
+
23
+ # attributes
24
+ field :_slug, default: ''
25
+
26
+ # callbacks
27
+ before_save :_populate_custom_slug
28
+
29
+
30
+ # helpers
31
+ def _populate_custom_slug
32
+ self.slugs = []
33
+
34
+ if ! self._slug.empty?
35
+ append_slug(self._slug)
36
+ end
37
+
38
+ return true
39
+ end
40
+
41
+
42
+ # override Mongoid::Slug method to workaround slug change
43
+ def slug_builder
44
+ _cur_slug = nil
45
+
46
+ # if custom slug is defined use that
47
+ if ! self._slug.empty?
48
+ append_slug(self._slug)
49
+ end
50
+
51
+ # user defined slug
52
+ if new_with_slugs? or persisted_with_slug_changes?
53
+ _cur_slug = _slugs.last
54
+ end
55
+
56
+ # generate slug if the slug is no custom slug defined or does not exist
57
+ _cur_slug || pre_slug_string
58
+ end
59
+
60
+
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
67
+
@@ -0,0 +1,114 @@
1
+ # =============================================================================
2
+ #
3
+ # class Book
4
+ # include Mongoid::Document
5
+ # include Mongoid::SortedRelations
6
+ #
7
+ # field :title
8
+ #
9
+ # has_many :chapters
10
+ # sorted_relations_for :chapters
11
+ #
12
+ # has_and_belongs_to_many :authors
13
+ # sorted_relations_for :authors
14
+ # end
15
+ #
16
+ # b = Book.create('Hello')
17
+ #
18
+ # b.sorted_chapter_ids = [ Chapter.create(:title => 'The End').id,
19
+ # Chapter.create(:title => 'Part 3' ).id,
20
+ # Chapter.create(:title => 'Part 2' ).id,
21
+ # Chapter.create(:title => 'Part 1' ).id,
22
+ # Chapter.create(:title => 'Intro' ).id ]
23
+ #
24
+ # b.sorted_chapters()
25
+ #
26
+ # book.sorted_author_ids = [ Author.create(:name => 'Sun Tzu').id,
27
+ # Author.create(:name => 'Sun Wu' ).id,
28
+ # Author.create(:name => 'Lao Zi' ).id ]
29
+ #
30
+ # book.sorted_authors()
31
+ #
32
+ # =============================================================================
33
+
34
+ module Ants
35
+ module SortedRelations
36
+ extend ActiveSupport::Concern
37
+
38
+ class_methods do
39
+
40
+ def sorted_relations_for(relation_name)
41
+ relation = self.relations[relation_name.to_s]
42
+
43
+ if relation.macro == :has_and_belongs_to_many
44
+ define_has_and_belongs_to_many_sorted_methods(relation)
45
+ end
46
+
47
+ if relation.macro == :has_many
48
+ define_has_many_sorted_methods(relation)
49
+ end
50
+ end
51
+
52
+
53
+ #
54
+ # HAS_AND_BELONGS_TO_MANY
55
+ #
56
+ def define_has_and_belongs_to_many_sorted_methods(relation)
57
+ # sorted_#{ relation.name }
58
+ self.send(:define_method, "sorted_#{ relation.name }") do
59
+ related_documents = self.send(relation.name)
60
+ sorted_relation_ids = self.send("#{ relation.name.to_s.singularize }_ids").map { |id| id.to_s }
61
+
62
+ sorted_documents = related_documents.sort_by do |document|
63
+ sorted_relation_ids.index(document.id.to_s)
64
+ end
65
+
66
+ return Mongoid::FakeCriteria.new(sorted_documents)
67
+ end
68
+
69
+ # sorted_#{ relation.name } = [ <array of ids> ]
70
+ self.send(:define_method, "sorted_#{ relation.name.to_s.singularize }_ids=") do |ids|
71
+ self.send("#{ relation.name.to_s.singularize }_ids=", ids)
72
+ end
73
+ end
74
+
75
+
76
+ #
77
+ # HAS_MANY
78
+ #
79
+ def define_has_many_sorted_methods(relation)
80
+ # define field to store order for has_many related documents
81
+ self.field "_sorted_#{ relation.name.to_s.singularize }_ids", :type => Array, default: []
82
+
83
+ # sorted_#{ relation.name }
84
+ self.send(:define_method, "sorted_#{ relation.name }") do
85
+ related_documents = self.send(relation.name)
86
+ sorted_relation_ids = self["_sorted_#{ relation.name.to_s.singularize }_ids"].map { |id| id.to_s }
87
+
88
+ # missing or new ids are in random order
89
+ sorted_documents = related_documents.sort_by do |document|
90
+ sorted_relation_ids.index(document.id.to_s)
91
+ end
92
+
93
+ return Mongoid::FakeCriteria.new(sorted_documents)
94
+ end
95
+
96
+ # sorted_#{ relation.name } = [ <array of ids> ]
97
+ self.send(:define_method, "sorted_#{ relation.name.to_s.singularize }_ids=") do |ids|
98
+ ids = ids.map{ |id| id.to_s }
99
+
100
+ self.update_attributes({
101
+ "_sorted_#{ relation.name.to_s.singularize }_ids" => ids, # store order
102
+ "#{ relation.name.to_s.singularize }_ids" => ids # update relations
103
+ })
104
+ end
105
+ end
106
+
107
+
108
+ end
109
+ end
110
+ end
111
+
112
+
113
+
114
+
@@ -0,0 +1,29 @@
1
+ module Mongoid
2
+ class FakeCriteria
3
+
4
+ def initialize(documents)
5
+ @documents = documents
6
+ end
7
+
8
+
9
+ def limit(quantity)
10
+ Mongoid::FakeCriteria.new(@documents[0..quantity-1])
11
+ end
12
+
13
+
14
+ def raw
15
+ @documents
16
+ end
17
+
18
+
19
+ def as_json(options={})
20
+ @documents.as_json(options)
21
+ end
22
+
23
+
24
+ def method_missing(*args, &block)
25
+ @documents.send(*args, &block)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongoid::FakeCriteria do
4
+
5
+ context "with the books ['book 1', 'book 2', 'book 3', 'book 4']" do
6
+ let(:books) { Mongoid::FakeCriteria.new(['book 1', 'book 2', 'book 3', 'book 4']) }
7
+
8
+ describe "#limit" do
9
+ the ("books.limit(2).raw") { should eql ['book 1', 'book 2'] }
10
+ the ("books.limit(0).raw") { should eql ['book 1', 'book 2', 'book 3', 'book 4'] }
11
+ the ("books.limit(6).raw") { should eql ['book 1', 'book 2', 'book 3', 'book 4'] }
12
+ end
13
+
14
+ context "#count" do
15
+ the ("books.count") { should eql 4 }
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ants::SortedRelations do
4
+
5
+ context "Having the book The Art of War" do
6
+ let(:book) { Book.new(:title => 'The Art of War') }
7
+
8
+ context "written by the authors Lao Zi, Sun Wu and Sun Tzu." do
9
+ before { book.sorted_author_ids = [ Author.create(:name => 'Sun Tzu').id,
10
+ Author.create(:name => 'Sun Wu' ).id,
11
+ Author.create(:name => 'Lao Zi' ).id ] }
12
+ before { book.save and book.reload }
13
+ before { book.sorted_author_ids = book.author_ids.map { |id| id.to_s }.reverse }
14
+
15
+ the("book.sorted_authors.map(&:name)") { should eql ['Lao Zi', 'Sun Wu', 'Sun Tzu'] }
16
+ end
17
+ end
18
+
19
+ context "Having the book War and Piece" do
20
+ let(:book) { Book.create(:title => 'War and Piece') }
21
+
22
+ context "to have chapters Intro, Part 1, Part 2, Part 3 and The End." do
23
+ before { book.sorted_chapter_ids = [ Chapter.create(:title => 'The End').id,
24
+ Chapter.create(:title => 'Part 3').id,
25
+ Chapter.create(:title => 'Part 2').id,
26
+ Chapter.create(:title => 'Part 1').id,
27
+ Chapter.create(:title => 'Intro').id ] }
28
+ before { book.reload }
29
+ before { book.sorted_chapter_ids = book.chapter_ids.map { |id| id.to_s }.reverse }
30
+
31
+ the("book.sorted_chapters.map(&:title)") { should eql ['Intro', 'Part 1', 'Part 2', 'Part 3', 'The End'] }
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rails/all'
4
+
5
+ require 'rspec'
6
+ require 'rspec-aspic'
7
+
8
+ require 'mongoid'
9
+ require File.expand_path('../../lib/ants', __FILE__)
10
+
11
+
12
+ Mongoid.configure do |config|
13
+ config.connect_to('mongoid_ants_test')
14
+ end
15
+
16
+ Dir[ "#{File.dirname(__FILE__)}/support/**/*.rb" ].each { |f| require f }
17
+
18
+ RSpec.configure do |config|
19
+ config.mock_with :rspec
20
+ config.include RSpecAspic
21
+ config.after :each do
22
+ Mongoid.purge!
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ class Author
2
+ include Mongoid::Document
3
+
4
+ # attributes
5
+ field :name
6
+
7
+ # relations
8
+ has_and_belongs_to_many :books
9
+ end
@@ -0,0 +1,14 @@
1
+ class Book
2
+ include Mongoid::Document
3
+ include Ants::SortedRelations
4
+
5
+ # attributes
6
+ field :title
7
+
8
+ # relations
9
+ has_many :chapters
10
+ sorted_relations_for :chapters
11
+
12
+ has_and_belongs_to_many :authors
13
+ sorted_relations_for :authors
14
+ end
@@ -0,0 +1,9 @@
1
+ class Chapter
2
+ include Mongoid::Document
3
+
4
+ # attributes
5
+ field :title
6
+
7
+ # relations
8
+ belongs_to :book
9
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ants
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Kravets
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mongoid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mongoid-slug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '4.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '4.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.7
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-aspic
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 0.0.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.0.2
97
+ description: |
98
+ Collection of concerns and helpers for Rails + Mongoid + Character web development
99
+ email: alex@slatestudio.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - Gemfile
106
+ - Gemfile.lock
107
+ - LICENSE.md
108
+ - README.md
109
+ - Rakefile
110
+ - ants.gemspec
111
+ - lib/ants.rb
112
+ - lib/ants/engine.rb
113
+ - lib/ants/version.rb
114
+ - lib/concerns/ants/featurable.rb
115
+ - lib/concerns/ants/hideable.rb
116
+ - lib/concerns/ants/id.rb
117
+ - lib/concerns/ants/meta.rb
118
+ - lib/concerns/ants/orderable.rb
119
+ - lib/concerns/ants/publication.rb
120
+ - lib/concerns/ants/slug.rb
121
+ - lib/concerns/ants/sorted_relations.rb
122
+ - lib/mongoid/fake_criteria.rb
123
+ - spec/mongoid/fake_criteria_spec.rb
124
+ - spec/mongoid/sorted_relations_spec.rb
125
+ - spec/spec_helper.rb
126
+ - spec/support/models/author.rb
127
+ - spec/support/models/book.rb
128
+ - spec/support/models/chapter.rb
129
+ homepage: https://github.com/slate-studio/ants
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project: ants
149
+ rubygems_version: 2.4.6
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Collection of concerns for Rails + Mongoid + Character web development
153
+ test_files: []