prim 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ Overview
2
+ --------
3
+
4
+ __Prim__ gives you the power to assign and manage the primary member of any Rails one–to–many or many–to–many assocation. And it's really simple to get started! Let's say we needed to model a User who could have several Languages:
5
+
6
+ ```ruby
7
+ class User < ActiveRecord::Base
8
+ has_many :user_languages
9
+ has_many :languages, through: :user_languages
10
+
11
+ ...
12
+ end
13
+ ```
14
+
15
+ ```ruby
16
+ class UserLanguage < ActiveRecord::Base
17
+ belongs_to :user
18
+ belongs_to :language
19
+
20
+ ...
21
+ end
22
+ ```
23
+
24
+ ```ruby
25
+ class Language < ActiveRecord::Base
26
+ has_many :user_languages
27
+ has_many :users, through: :user_languages
28
+
29
+ ...
30
+ end
31
+ ```
32
+
33
+ Great! Now let's say we've added a specific set of Languages. Our Users can now have new Language associations by simply creating a record in the `user_languages` mapping table, relating a User and a Language. But what if we want to know which of a User's Languages is their most important? Well, we could add a `sort_order` or `primary` column to the `user_languages` table, but then we'll need to write code to manage it all.
34
+
35
+ Enter __Prim__.
36
+
37
+ With __Prim__ we can just add a line of code to the User model...
38
+
39
+ ```ruby
40
+ class User < ActiveRecord::Base
41
+ ...
42
+
43
+ has_primary :language
44
+ end
45
+ ```
46
+
47
+ ...and run a migration:
48
+
49
+ ```ruby
50
+ class AddPrimaryToUserLanguages < ActiveRecord::Migration
51
+ def change
52
+ add_column :user_languages, :primary, :boolean, { default: false }
53
+ end
54
+ end
55
+ ```
56
+
57
+ And we're done! Now we can set any User's primary language...
58
+
59
+ ```ruby
60
+ User.first.primary_language = Language.where( name: "English" )
61
+ ```
62
+
63
+ ...and retrieve it:
64
+
65
+ ```ruby
66
+ User.first.primary_language
67
+ => #<Language id: 5, name: "English" ... >
68
+ ```
69
+
70
+ Contributing
71
+ ------------
72
+
73
+ Want to contribute? Find a TODO or [Github issue](https://github.com/OrcaHealth/prim/issues) and take care of it! Or suggest a feature or file a bug on the [issues page](https://github.com/OrcaHealth/prim/issues). Just pack up your commits by rebasing them, then submit a pull request!
74
+
data/lib/prim.rb CHANGED
@@ -15,6 +15,7 @@ module Prim
15
15
  module ClassMethods
16
16
  include Prim::Helpers
17
17
 
18
+ # TODO: allow multiple singular names in one call.
18
19
  def has_primary name, options = {}
19
20
  singular_name = name.to_sym
20
21
  association_name = plural_sym(singular_name)
@@ -36,5 +37,5 @@ module Prim
36
37
  end
37
38
 
38
39
  class SingularAssociationError < StandardError; end
39
- class MissingColumnError < StandardError; end
40
+ class InvalidPrimaryColumnError < StandardError; end
40
41
  end
@@ -13,6 +13,9 @@ module Prim
13
13
  def initialize relationship, instance
14
14
  @instance = instance
15
15
  @relationship = relationship
16
+
17
+ # Attach this collection to the mapping class so it has access to static methods.
18
+ relationship.reflected_class.prim_collection = self
16
19
  end
17
20
 
18
21
  def primary
@@ -37,6 +40,18 @@ module Prim
37
40
  true
38
41
  end
39
42
 
43
+ def siblings_for mapping
44
+ foreign_key = relationship.mapping_reflection.foreign_key
45
+ mapping_type = relationship.mapping_reflection.type
46
+ mapping_class = relationship.reflected_class
47
+ primary_key = mapping_class.primary_key
48
+
49
+ query = relationship.reflected_class.where( foreign_key => mapping[ foreign_key ] )
50
+ query = query.where( mapping_type => mapping[ mapping_type ] ) unless mapping_type.nil?
51
+
52
+ query.where( mapping_class.arel_table[ primary_key ].not_eq( mapping[ primary_key ] ) )
53
+ end
54
+
40
55
  private
41
56
 
42
57
  # Creates a new source record and a mapping between it and the owner instance.
@@ -23,15 +23,17 @@ module Prim
23
23
  extend ActiveSupport::Concern
24
24
 
25
25
  included do
26
-
27
26
  validate :only_one_primary
27
+ end
28
28
 
29
- def only_one_primary
30
- if self[:primary]
31
- siblings.update_all('"primary" = false')
32
- end
29
+ def only_one_primary
30
+ if self[:primary]
31
+ siblings.update_all('"primary" = false')
33
32
  end
33
+ end
34
34
 
35
+ def siblings
36
+ prim_collection.siblings_for self
35
37
  end
36
38
  end
37
39
 
@@ -9,52 +9,32 @@ module Prim
9
9
  delegate :source_reflection, :through_reflection, :foreign_key, to: :reflection
10
10
 
11
11
  def initialize association_name, owning_class, options = {}
12
- @options = extract_options options
12
+ @options = options
13
13
  @association_name = association_name
14
14
  @owning_class = owning_class
15
15
  @reflection = owning_class.reflect_on_association( association_name )
16
16
 
17
+ # TODO: remove these exceptions and replace with logged errors? hmm.
17
18
  if reflection.nil?
18
- raise ArgumentError.new("Prim: Association '#{ association_name }' not found " +
19
+ raise ArgumentError.new("Association '#{ association_name }' not found " +
19
20
  "on #{ owning_class.name }. Perhaps you misspelled it?")
20
21
 
21
22
  elsif !reflection.collection?
22
- raise SingularAssociationError.new("Prim: Association '#{ association_name }' " +
23
+ raise SingularAssociationError.new("Association '#{ association_name }' " +
23
24
  "is not a one-to-many or many-to-many relationship, so it can't have a primary.")
24
25
 
25
- elsif !reflected_column_names.include? "primary"
26
- raise MissingColumnError.new("Prim: #{ owning_class.name } needs table " +
27
- "`#{ mapping_reflection.table_name }` to have a boolean 'primary' column in " +
28
- "order to have a primary #{ source_class.name }.")
26
+ elsif !primary_column
27
+ # TODO: add a generator to automatically create a migration, then change this
28
+ # message to give users the exact instruction needed.
29
+ raise InvalidPrimaryColumnError.new("#{ owning_class.name } needs table " +
30
+ "`#{ mapping_reflection.table_name }` to have a boolean 'primary' column " +
31
+ "in order to have a primary #{ source_class.name }.")
29
32
  end
30
33
 
31
34
  # TODO: ensure the association isn't nested?
32
35
 
33
- configure_reflected_class!
34
-
35
- true
36
- end
37
-
38
- def configure_reflected_class!
39
- foreign_key = mapping_reflection.foreign_key
40
- mapping_type = mapping_reflection.type
41
-
42
- load_siblings = lambda do
43
- primary_key = self.class.primary_key
44
- query = self.class.where( foreign_key => self[ foreign_key ] )
45
-
46
- if mapping_type
47
- query = query.where( mapping_type => self[ mapping_type ] )
48
- end
49
-
50
- query.where( self.class.arel_table[ primary_key ].not_eq(self[ primary_key ]) )
51
- end
52
-
53
- reflected_class.class_eval do
54
- define_method :siblings, &load_siblings
55
- end
56
-
57
36
  reflected_class.send :include, InstanceMethods::Reflected
37
+ reflected_class.class_attribute :prim_collection
58
38
  end
59
39
 
60
40
  # The association method to call on the owning class to retrieve a record's collection.
@@ -78,22 +58,6 @@ module Prim
78
58
  through_reflection || source_reflection
79
59
  end
80
60
 
81
- # True if the `mapping_reflection` class has an "inverse" mapping back to the owning
82
- # class with a matching name. Verifies that a polymorphic mapping exists.
83
- # def polymorphic_mapping?
84
- # if polymorphic_as.present?
85
- # !!reflected_class.reflect_on_all_associations.detect do |refl|
86
- # refl.name == polymorphic_as and refl.association_class == ActiveRecord::Associations::BelongsToPolymorphicAssociation
87
- # end
88
- # end
89
- # end
90
-
91
- # The name the owning class uses to create mappings in the reflected class; i.e. the
92
- # `:as` option set on the `has_many` association in the owner.
93
- # def reflection_polymorphic_as
94
- # mapping_reflection.options[:as].to_sym
95
- # end
96
-
97
61
  # True if this relationship relies on a mapping table for `primary` records.
98
62
  def mapping_table?
99
63
  !!through_reflection
@@ -101,13 +65,8 @@ module Prim
101
65
 
102
66
  private
103
67
 
104
- # The columns of the reflected class (where `primary` needs to be).
105
- def reflected_column_names
106
- reflected_class.column_names
107
- end
108
-
109
- def extract_options options
110
- options
68
+ def primary_column
69
+ reflected_class.columns.find { |col| col.name == "primary" }
111
70
  end
112
71
  end
113
72
  end
data/prim.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "prim"
3
- s.version = "0.0.2"
3
+ s.version = "0.0.3"
4
4
  s.date = "2012-12-19"
5
5
  s.summary = "Easily manage Rails associations that need a primary member."
6
6
  s.description = "With Prim it's easy to add a primary member to any one-to-many or many-to-many association. " +
@@ -10,5 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.files = `git ls-files`.split("\n")
11
11
  s.homepage = "https://github.com/orcahealth/prim"
12
12
 
13
+ s.add_dependency "activerecord", "~> 3.2.0"
14
+
13
15
  s.require_paths = [ "lib" ]
14
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,23 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
  date: 2012-12-19 00:00:00.000000000 Z
13
- dependencies: []
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ prerelease: false
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.0
22
+ none: false
23
+ type: :runtime
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: 3.2.0
29
+ none: false
14
30
  description: With Prim it's easy to add a primary member to any one-to-many or many-to-many
15
31
  association. Just add a short configuration to a model, generate and run a migration,
16
32
  and you're all set.
@@ -19,15 +35,14 @@ executables: []
19
35
  extensions: []
20
36
  extra_rdoc_files: []
21
37
  files:
38
+ - README.md
22
39
  - lib/prim.rb
23
40
  - lib/prim/collection.rb
24
41
  - lib/prim/connector.rb
25
42
  - lib/prim/helpers.rb
26
43
  - lib/prim/instance_methods.rb
27
- - lib/prim/primary.rb
28
44
  - lib/prim/railtie.rb
29
45
  - lib/prim/relationship.rb
30
- - lib/tasks/prim.rake
31
46
  - prim.gemspec
32
47
  homepage: https://github.com/orcahealth/prim
33
48
  licenses: []
data/lib/prim/primary.rb DELETED
@@ -1,11 +0,0 @@
1
- module Prim
2
- class Primary
3
- attr_reader :relationship, :record
4
-
5
- def initialize relationship, record
6
- @relationship = relationship
7
- @record = record
8
-
9
- end
10
- end
11
- end
data/lib/tasks/prim.rake DELETED
File without changes