ants 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.
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: []