collection_extensions 0.0.1 → 1.0.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.
@@ -1,10 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
6
- - jruby-18mode
7
5
  - jruby-19mode
8
- - rbx-18mode
9
6
  - rbx-19mode
10
- - ree
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in collection_extensions.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'activerecord', '~> 3.2'
7
+
6
8
  group :test do
7
9
  gem "rake", "~> 0.9.2"
8
10
  gem 'rspec', '~> 2.11.0'
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # CollectionExtensions
2
2
 
3
3
  Sometimes an operation just doesn't fit well into a scope, but you don't want to lose your declarative code style
4
- by operating on all the objects individually. This gem adds a few lines of code to make it easier to add methods to collections of objects.
4
+ by operating on all the objects individually. This gem adds a few lines of code to make it easier to add methods
5
+ to collections of ActiveRecord objects.
5
6
 
6
7
  <img src="https://secure.travis-ci.org/arches/collection_extensions.png" />
7
8
 
@@ -9,44 +10,56 @@ by operating on all the objects individually. This gem adds a few lines of code
9
10
 
10
11
  Add this line to your application's Gemfile:
11
12
 
12
- gem 'collection_extensions'
13
+ ```ruby
14
+ gem 'collection_extensions'
15
+ ```
13
16
 
14
17
  And then execute:
15
18
 
16
- $ bundle
19
+ ```bash
20
+ $ bundle
21
+ ```
17
22
 
18
23
  Or install it yourself as:
19
24
 
20
- $ gem install collection_extensions
25
+ ```bash
26
+ $ gem install collection_extensions
27
+ ```
21
28
 
22
29
  ## Usage
23
30
 
24
- Include the module in your model and specify what collections you want to extend:
31
+ A collections of records will be extended based on the naming convention `%sCollectionExtensions`, where the `%s` substitution
32
+ is the camel-cased name of the class. For example, collections of User objects will be extended with the methods in the
33
+ `UserCollectionExtensions` model and collections of BlogPost objects will be extended with the methods in the `BlogPostCollectionExtensions`
34
+ module.
25
35
 
26
- class User < ActiveRecord::Base
27
- include CollectionExtensions
28
- has_many :orders, :preferences
29
- extend_collections :orders, :preferences
30
-
31
- # the original unextended collection is still available, aliased as orig_* (eg, orig_orders or orig_preferences)
32
- end
33
-
34
- This will use the modules OrdersCollectionExtensions and PreferencesCollectionExtensions to extend those associations
35
- whenever you use them. For example:
36
+ ```ruby
37
+ module BlogPostCollectionExtensions
36
38
 
37
- module OrdersCollectionExtensions
38
- def for_product_id(product_id)
39
- # 'self' is the array of orders
40
- select {|o| o.line_items.collect(&:product_id).include? product_id}
39
+ # hash key is user ID, hash value is the number of comments they've posted on this set of blog posts
40
+ def comments_per_user
41
+ comment_counts = {}
42
+
43
+ # 'self' is the array of blog posts
44
+ each do |blog_post|
45
+ blog_post.comments.each do |comment|
46
+ comment_counts[comment.user_id] ||= 0
47
+ comment_counts[comment.user_id] += 1
41
48
  end
42
49
  end
43
50
 
44
- > User.find(1).orders.for_product_id(2)
51
+ comment_counts
52
+ end
53
+ end
54
+ ```
45
55
 
46
- If you want a different naming convention, set the config variable using `%s` string substitution:
56
+ ### Naming Convention
47
57
 
48
- CollectionExtensions::Config.naming_convention = "MethodsForCollectionsOf%s"
58
+ If you want a different naming convention, set the config variable using `%s` string substitution:
49
59
 
60
+ ```ruby
61
+ CollectionExtensions::Config.naming_convention = "MethodsForCollectionsOf%s"
62
+ ```
50
63
 
51
64
  ## Contributing
52
65
 
@@ -1,25 +1,32 @@
1
1
  require "collection_extensions/version"
2
2
  require "collection_extensions/cattr"
3
3
  require "collection_extensions/config"
4
+ require "active_record"
4
5
 
5
- module CollectionExtensions
6
+ class ActiveRecord::Relation
7
+ alias :orig_to_a :to_a
8
+ alias :orig_method_missing :method_missing
6
9
 
7
- def self.included(base)
8
- base.extend ClassMethods
9
- end
10
+ def to_a
11
+ records = orig_to_a
12
+
13
+ records.extend extension_module if extension_module
10
14
 
11
- module ClassMethods
12
- def extend_collections(*associations)
13
- Array(associations).each do |association|
14
- alias_method "orig_#{association}", association
15
+ records
16
+ end
15
17
 
16
- define_method association do
17
- upper_camel = association.to_s.split("_").collect{|piece| piece.capitalize}.join
18
- extender = Object.const_get(CollectionExtensions::Config.naming_convention % upper_camel)
19
- send("orig_#{association}").extend(extender)
20
- end
21
- end
18
+ def extension_module
19
+ extension_klass = CollectionExtensions::Config.naming_convention % @klass.to_s
20
+ if Object.constants.include? extension_klass.to_sym
21
+ Object.const_get(extension_klass)
22
22
  end
23
23
  end
24
24
 
25
+ def method_missing(method, *args, &block)
26
+ if extension_module and extension_module.method_defined? method
27
+ to_a.send(method, *args, &block)
28
+ else
29
+ orig_method_missing(method, *args, &block)
30
+ end
31
+ end
25
32
  end
@@ -1,3 +1,3 @@
1
1
  module CollectionExtensions
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,69 +1,67 @@
1
+ require 'active_record'
1
2
  require 'collection_extensions'
2
3
 
3
- module ExamplesCollectionExtensions
4
- def operate_on_all_examples
5
- "operate_on_all_examples"
4
+ module UserCollectionExtensions
5
+ def utest
6
+ "ufoo"
6
7
  end
7
8
  end
8
9
 
9
- module FoobarsCollectionExtensions
10
- def operate_on_all_foobars
11
- "operate_on_all_foobars"
12
- end
10
+ module ExtensionMethodsForUserCollections
13
11
  end
14
12
 
15
- module MethodsForCollectionsOfExamples
16
- def operate_on_all_examples
17
- "from a module with a different naming convention"
18
- end
19
- end
20
-
21
- class CollectionTester
22
- include CollectionExtensions
23
-
24
- def examples
25
- "original examples method"
13
+ describe "#extension_class" do
14
+ context "when the extension module exists" do
15
+ it "returns the constant" do
16
+ r = ActiveRecord::Relation.new :User, :users
17
+ r.extension_module.should == UserCollectionExtensions
18
+ end
26
19
  end
27
20
 
28
- def foobars
29
- "original foobars method"
21
+ context "when the extension module doesn't exist" do
22
+ it "returns nil" do
23
+ r = ActiveRecord::Relation.new :Order, :orders
24
+ r.extension_module.should be_nil
25
+ end
30
26
  end
31
27
 
32
- extend_collections :examples, :foobars
33
- end
34
-
35
- describe CollectionExtensions do
36
- let(:ct) { CollectionTester.new }
28
+ context "when the naming convention has been changed" do
29
+ before { CollectionExtensions::Config.naming_convention = "ExtensionMethodsForUserCollections" }
30
+ after { CollectionExtensions::Config.naming_convention = CollectionExtensions::Config::DEFAULT_NAMING_CONVENTION }
37
31
 
38
- describe "#extend_collections" do
39
- it "aliases the examples" do
40
- ct.orig_examples.should == "original examples method"
41
- ct.examples.should be_a ExamplesCollectionExtensions
42
- ct.examples.operate_on_all_examples.should == "operate_on_all_examples"
43
- end
44
-
45
- it "aliases the foobars" do
46
- ct.orig_foobars.should == "original foobars method"
47
- ct.foobars.should be_a FoobarsCollectionExtensions
48
- ct.foobars.operate_on_all_foobars.should == "operate_on_all_foobars"
32
+ it "uses the proper convention to return the constant" do
33
+ r = ActiveRecord::Relation.new :User, :users
34
+ r.extension_module.should == ExtensionMethodsForUserCollections
49
35
  end
50
36
  end
51
37
  end
52
38
 
53
- describe "changing the naming convention" do
54
- let(:ct) { CollectionTester.new }
55
-
56
- before do
57
- CollectionTester::Config.naming_convention = "MethodsForCollectionsOf%s"
39
+ describe "calling methods on the array" do
40
+ it "pulls extension methods from the module" do
41
+ r = ActiveRecord::Relation.new :User, :users
42
+ r.stub(orig_to_a: [])
43
+ r.to_a.utest.should == "ufoo"
58
44
  end
45
+ end
59
46
 
60
- it "extends the association with the proper module" do
61
- ct.orig_examples.should == "original examples method"
62
- ct.examples.should be_a MethodsForCollectionsOfExamples
63
- ct.examples.operate_on_all_examples.should == "from a module with a different naming convention"
47
+ describe "calling methods on the relation" do
48
+ it "pulls extension methods from the module" do
49
+ r = ActiveRecord::Relation.new :User, :users
50
+ r.stub(orig_to_a: [])
51
+ r.utest.should == "ufoo"
64
52
  end
65
- end
66
53
 
67
- describe "" do
54
+ it "lets non-extension methods pass through" do
55
+ r = ActiveRecord::Relation.new :User, :users
56
+ r.stub(orig_to_a: ['first'])
57
+ r.first.should == 'first'
58
+ end
68
59
 
60
+ context "when the extension module doesn't exist" do
61
+ it "lets methods pass through" do
62
+ r = ActiveRecord::Relation.new :Order, :orders
63
+ r.stub(orig_to_a: ['first'])
64
+ r.first.should == 'first'
65
+ end
66
+ end
69
67
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collection_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-04 00:00:00.000000000 Z
12
+ date: 2012-09-05 00:00:00.000000000Z
13
13
  dependencies: []
14
14
  description: Lightweight framework for adding methods to groups of ActiveRecord objects
15
15
  email: