friendly-attributes 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,28 +1,33 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ### 0.7.0
5
+ * (ihoka,cristi) Support for associating multiple FriendlyAttributes models with a single ActiveRecord model
6
+ * (ihoka,cristi) FriendlyAttributes::Details renamed to FriendlyAttributes::Base
7
+ * (ihoka,cristi) Documentation
8
+
4
9
  ### 0.6.1
5
- * (ihoka) Extended #changed? on the ActiveRecord model to indicate the record has been changed if the associated Friendly has changed.
10
+ * (ihoka,cristi) Extended #changed? on the ActiveRecord model to indicate the record has been changed if the associated Friendly has changed.
6
11
 
7
12
  ### 0.6.0
8
- * (ihoka) Added #friendly_details_build_options method to the ActiveRecord model, allowing to specify default attributes when initially building the Friendly model.
13
+ * (ihoka,cristi) Added #friendly_details_build_options method to the ActiveRecord model, allowing to specify default attributes when initially building the Friendly model.
9
14
 
10
15
  ### 0.5.0
11
16
 
12
- * (ihoka) Added configurable active_record_key to the FriendlyDetails model. active_record_key affects the name of the generated Friendly index table and the attribute in which the ActiveRecord model ID is stored. It defaults to :active_record_id.
17
+ * (ihoka,cristi) Added configurable active_record_key to the FriendlyDetails model. active_record_key affects the name of the generated Friendly index table and the attribute in which the ActiveRecord model ID is stored. It defaults to :active_record_id.
13
18
 
14
19
  ### 0.4.0
15
20
 
16
- * (ihoka) Added #attributes method to FriendlyAttributes::Details base class.
21
+ * (ihoka,cristi) Added #attributes method to FriendlyAttributes::Details base class.
17
22
 
18
23
  ### 0.3.2
19
24
 
20
- * (ihoka) Added description to spec matcher.
25
+ * (ihoka,cristi) Added description to spec matcher.
21
26
 
22
27
  ### 0.3.1
23
28
 
24
- * (ihoka) added spec matchers
29
+ * (ihoka,cristi) added spec matchers
25
30
 
26
31
  ### 0.3.0
27
32
 
28
- * (ihoka) Extended the DSL to allow passing a hash of options when defining friendly_attributes.
33
+ * (ihoka,cristi) Extended the DSL to allow passing a hash of options when defining friendly_attributes.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Istvan Hoka
1
+ Copyright (c) 2011 Aissac Labs
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Friendly Attributes
2
+
3
+ Extend your ActiveRecord models with attributes dynamically, without any schema migrations.
4
+
5
+ ## Description
6
+
7
+ [FriendlyORM][friendly] is an implementation of a document based storage on top of MySQL.
8
+
9
+ FriendlyAttributes associates FriendlyORM documents with your ActiveRecord models, handles delegation of attribute accessors and automatically saves and destroys the associated document.
10
+
11
+ ## Installation
12
+
13
+ Currently, only ActiveRecord 2.3.x is supported. ActiveRecord 3 is not supported yet.
14
+
15
+ Add it to your Gemfile:
16
+
17
+ gem 'friendly-attributes'
18
+
19
+ or, to your `config/environment.rb` in Rails:
20
+
21
+ config.gem 'friendly-attributes'
22
+
23
+ ## Usage
24
+
25
+ To use Friendly Attributes, you must have an existing ActiveRecord model:
26
+
27
+ # app/models/user.rb
28
+ class User < ActiveRecord::Base
29
+ # ...
30
+ end
31
+
32
+ Create a class for storing your Friendly document, extending `FriendlyAttributes::Base`:
33
+
34
+ # app/models/user_details.rb
35
+ class UserDetails < FriendlyAttributes::Base
36
+ end
37
+
38
+ `FriendlyAttributes::Base` mixes in `Friendly::Document`, so check out the [Friendly ORM documentation][friendly] for what options you can use here, like defining indexes and scopes.
39
+
40
+ Configure the attributes you need to extend your ActiveRecord model:
41
+
42
+ # app/models/account.rb
43
+ class User < ActiveRecord::Base
44
+ include FriendlyAttributes
45
+
46
+ friendly_details UserDetails, {
47
+ String => :github_username,
48
+ Integer => [:shoe_size, :birth_year]
49
+ }
50
+ end
51
+
52
+ If using Rails, configure the database to be used by Friendly, by creating `config/friendly.yml` and specifying the configuration. If you don't mind mixing tables, the database can be the same as the one configured for Rails, but it does not have to be. Note that Friendly automatically creates a table for each index you define in your Friendly document.
53
+
54
+ # config/friendly.yml
55
+ development:
56
+ adapter: mysql
57
+ socket: /tmp/mysql.sock
58
+ database: friendly_database_development
59
+ username: root
60
+ password:
61
+
62
+ Use the Rails console or a rake task to create the Friendly document tables:
63
+
64
+ Friendly.create_tables!
65
+
66
+ Once complete, you can use your Friendly Attributes just like you would regular ActiveRecord attributes:
67
+
68
+ user = User.first
69
+ user.shoe_size = 42
70
+ user.save!
71
+
72
+ ## Documentation
73
+
74
+ Check out the source documentation for more information about usage.
75
+
76
+ ## Development
77
+
78
+ In order to setup a development environment and run the specs, you need Bundler:
79
+
80
+ gem install bundler
81
+
82
+ Then, install the dependencies:
83
+
84
+ bundle install
85
+
86
+ Create a database to use for runnning specs:
87
+
88
+ mysqladmin create friendly_attributes_test
89
+
90
+ Copy spec/config.yml.example and customize it with the database you have created:
91
+
92
+ cp spec/config.yml.sample spec/config.yml
93
+
94
+ Run the specs:
95
+
96
+ rake spec
97
+
98
+ ## Credits
99
+
100
+ Friendly Attributes was developed by [Istvan Hoka][ihoka] and [Cristi Duma][cduma].
101
+
102
+ Copyright (c) 2011 Aissac Labs. See [LICENSE.txt][license] for further details.
103
+
104
+ [github]: http://github.com/ihoka/friendly-attributes
105
+ [friendly]: http://github.com/jamesgolick/friendly
106
+ [ihoka]: http://istvanhoka.com
107
+ [cduma]: http://twitter.com/cristiduma
108
+ [blinksale]: http://www.blinksale.com
109
+ [license]: LICENSE.txt
@@ -4,8 +4,9 @@ require 'active_record'
4
4
  module FriendlyAttributes
5
5
  autoload :ClassMethods, 'friendly_attributes/class_methods'
6
6
  autoload :InstanceMethods, 'friendly_attributes/instance_methods'
7
- autoload :Details, 'friendly_attributes/details'
7
+ autoload :Base, 'friendly_attributes/base'
8
8
  autoload :DetailsDelegator, 'friendly_attributes/details_delegator'
9
+ autoload :Configuration, 'friendly_attributes/configuration'
9
10
 
10
11
  module Test
11
12
  autoload :Matchers, 'friendly_attributes/test/matchers'
@@ -0,0 +1,49 @@
1
+ module FriendlyAttributes
2
+ class Base
3
+ class << self
4
+ # Finds an existing Friendly model, associated with an ActiveRecord model,
5
+ # or builds a new Friendly model, using the specified options.
6
+ #
7
+ # @param [Integer] ActiveRecord model ID for which to find the associated Friendly model
8
+ # @param [Hash] options
9
+ # @return [FriendlyAttributes::Base] found or new record
10
+ def find_or_build_by_active_record_id(active_record_id, options={})
11
+ active_record_id && first(active_record_key => active_record_id) || new(options.merge(active_record_key => active_record_id))
12
+ end
13
+ end
14
+
15
+ # Set the ID of the associated ActiveRecord model.
16
+ #
17
+ # Uses the :active_record_key property of the model.
18
+ #
19
+ # @param [String, Integer] active_record_id value to set the attribute
20
+ # @return [String, Integer] value
21
+ def write_active_record_id(active_record_id)
22
+ send(:"#{active_record_key}=", active_record_id)
23
+ end
24
+
25
+ # Get the ID of the associated ActiveRecord model.
26
+ #
27
+ # Uses the :active_record_key property of the model.
28
+ #
29
+ # @return [Integer] ActiveRecord ID
30
+ def read_active_record_id
31
+ send(active_record_key)
32
+ end
33
+
34
+ # Save the FriendlyAttribute model if it has been changed.
35
+ # Before saving, it sets the specified active_record_id, to handle the case when it is a new record or has been reassigned.
36
+ #
37
+ # @param [Integer] active_record_id ID of the associated ActiveRecord model
38
+ # @return [true, false] result of saving the record
39
+ def update_if_changed_with_model(active_record_id)
40
+ write_active_record_id(active_record_id) unless read_active_record_id == active_record_id
41
+ save if changed?
42
+ end
43
+
44
+ # Alias for Friendly::Document#to_hash
45
+ def attributes
46
+ to_hash
47
+ end
48
+ end
49
+ end
@@ -1,5 +1,15 @@
1
1
  module FriendlyAttributes
2
2
  module ClassMethods
3
+ # Configure a Friendly Base model associated with an ActiveRecord model.
4
+ #
5
+ # @overload friendly_details(klass, attributes)
6
+ # @param [Class] klass FriendlyAttributes::Base instance used to extend the ActiveRecord model
7
+ # @param [Hash] attributes hash of types and attributes names with which to extend the ActiveRecord, through FriendlyAttributes::Base
8
+ #
9
+ # @overload friendly_details(klass, attributes, options)
10
+ # @param [Hash] options configuration options for extending the FriendlyAttributes extension (see {DetailsDelegator#initialize})
11
+ #
12
+ # @return [DetailsDelegator]
3
13
  def friendly_details(*args, &block)
4
14
  klass = args.shift
5
15
  options = args.extract_options!
@@ -9,18 +19,9 @@ module FriendlyAttributes
9
19
  options = {}
10
20
  end
11
21
 
12
- delegate_options = proc {
13
- attributes.each do |key, value|
14
- if Array === value
15
- value.each { |v| delegated_attribute v, key }
16
- else
17
- delegated_attribute value, key
18
- end
19
- end
20
- }
21
-
22
- DetailsDelegator.new(klass, self, options, &block).tap do |dd|
23
- dd.instance_eval(&delegate_options)
22
+ DetailsDelegator.new(klass, self, attributes, options, &block).tap do |dd|
23
+ dd.setup_delegated_attributes
24
+ dd.instance_eval(&block) if block_given?
24
25
  end
25
26
  end
26
27
 
@@ -0,0 +1,35 @@
1
+ module FriendlyAttributes
2
+ class Configuration
3
+ attr_reader :details_delegators, :model, :attributes
4
+
5
+ def initialize(active_record_model)
6
+ @model = active_record_model
7
+ @details_delegators = []
8
+ @attributes = {}
9
+ end
10
+
11
+ def add(delegator)
12
+ details_delegators << delegator
13
+ end
14
+
15
+ def add_attribute(name, friendly_model)
16
+ attributes[name] = friendly_model
17
+ end
18
+
19
+ def model_for_attribute(attr)
20
+ attributes[attr]
21
+ end
22
+
23
+ def friendly_models
24
+ details_delegators.map { |dd| dd.friendly_model }
25
+ end
26
+
27
+ def map_models(&block)
28
+ friendly_models.map(&block)
29
+ end
30
+
31
+ def each_model(&block)
32
+ friendly_models.each(&block)
33
+ end
34
+ end
35
+ end
@@ -1,51 +1,121 @@
1
1
  module FriendlyAttributes
2
2
  class DetailsDelegator
3
- attr_reader :friendly_model, :ar_model
3
+ # @attr_reader [Class] friendly_model FriendlyAttributes::Base class towa which the attributes are delegated
4
+ # @attr_reader [Class] active_record_model The ActiveRecord::Base class from which the attributes are delegated
5
+ # @attr_reader [Hash<Symbol, Class>] delegated_attributes Attributes delegated by this DetailsDelegator
6
+ attr_reader :friendly_model, :active_record_model, :delegated_attributes, :attributes
4
7
 
5
8
  delegate :attribute, :indexes, :to => :friendly_model
6
9
 
7
- def initialize(friendly_model, ar_model, options={}, &block)
8
- @ar_model = ar_model
9
- @friendly_model = friendly_model
10
-
11
- _active_record_key = options.delete(:active_record_key) || :active_record_id
12
-
13
- friendly_model.instance_eval do
14
- include Friendly::Document
15
-
16
- attribute _active_record_key, Integer
17
- indexes _active_record_key
18
-
19
- cattr_accessor :active_record_key
10
+ class << self
11
+ # Method name for the FriendlyAttributes::Base class passed to it.
12
+ #
13
+ # @param [Class] klass class we want to get the name for
14
+ # @return [Symbol] underscored name of the class.
15
+ def friendly_model_name(klass)
16
+ klass.name.underscore.to_sym
20
17
  end
21
- friendly_model.active_record_key = _active_record_key
22
18
 
23
- ar_model.class_eval do
24
- cattr_accessor :friendly_model
25
-
26
- after_save :update_friendly_details
27
- after_destroy :destroy_friendly_details
28
-
29
- define_method(:details) do
30
- @details ||= friendly_model.find_or_build_by_active_record_id(id, friendly_details_build_options)
31
- end
19
+ # Instance variable name for the FriendlyAttributes::Base or `friendly_model_name` passed to it.
20
+ #
21
+ # @param [Symbol, String, Class] name_or_klass Class or name for which to generate the ivar name
22
+ # @return [Symbol] ivar name
23
+ def friendly_model_ivar(name_or_klass)
24
+ name_or_klass = friendly_model_name(name_or_klass) if name_or_klass.is_a?(Class)
25
+ :"@#{name_or_klass}_ivar"
32
26
  end
33
27
 
34
- ar_model.friendly_model = friendly_model
28
+ # Reader method name for a certain FriendlyAttributes::Base instance associated with the model.
29
+ #
30
+ # @param [Symbol, String, Class] name_or_klass Class or name for which to generate the reader method name
31
+ # @return [Symbol] reader method name
32
+ def friendly_model_reader(name_or_klass)
33
+ name_or_klass = friendly_model_name(name_or_klass) if name_or_klass.is_a?(Class)
34
+ :"load_#{name_or_klass}"
35
+ end
36
+ end
37
+
38
+ # Initialize new DetailsDelegator instance.
39
+ #
40
+ # @param [Class] friendly_model FriendlyAttributes model, that inherits from FriendlyModel::Base
41
+ # @param [Class] ar_model ActiveRecord model, host for the FriendlyAttributes model
42
+ # @param [Hash] attributes
43
+ # @param [Hash] options
44
+ # @option options [Symbol] :active_record_key (:active_record_id) name of the 'foreign key' in which the FriendlyModel::Base instance keeps a reference to the ActiveRecord model
45
+ def initialize(friendly_model, ar_model, attributes, options={})
46
+ @active_record_model = ar_model
47
+ @friendly_model = friendly_model
48
+ @attributes = attributes
49
+ @delegated_attributes = {}
35
50
 
36
- self.instance_eval(&block) if block_given?
51
+ setup_friendly_model(options.delete(:active_record_key) || :active_record_id)
52
+ setup_active_record_model
37
53
  end
38
54
 
39
55
  def delegated_attribute(name, klass)
56
+ delegated_attributes[name] = klass
57
+
40
58
  attribute name, klass
41
59
  delegated_method(:"#{name}")
42
60
  delegated_method(:"#{name}=")
61
+
62
+ active_record_model.friendly_attributes_configuration.add_attribute(name, friendly_model)
43
63
  end
44
64
 
45
65
  def delegated_method(name)
46
- ar_model.class_eval do
47
- delegate :"#{name}", :to => :details
66
+ friendly_model = self.friendly_model
67
+
68
+ active_record_model.class_eval do
69
+ delegate :"#{name}", :to => FriendlyAttributes::DetailsDelegator.friendly_model_reader(friendly_model)
48
70
  end
49
71
  end
72
+
73
+ def setup_delegated_attributes
74
+ attributes.each do |key, value|
75
+ if Array === value
76
+ value.each { |v| delegated_attribute v, key }
77
+ else
78
+ delegated_attribute value, key
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def setup_friendly_model(_active_record_key)
86
+ friendly_model.instance_eval do
87
+ include Friendly::Document
88
+
89
+ attribute _active_record_key, Integer
90
+ indexes _active_record_key
91
+
92
+ cattr_accessor :active_record_key
93
+ end
94
+ friendly_model.active_record_key = _active_record_key
95
+ end
96
+
97
+ def setup_active_record_model
98
+ friendly_model = self.friendly_model
99
+
100
+ active_record_model.class_eval do
101
+ # Stores the FriendlyAttributes configuration for the ActiveRecord model.
102
+ cattr_accessor :friendly_attributes_configuration
103
+
104
+ define_method(FriendlyAttributes::DetailsDelegator.friendly_model_reader(friendly_model)) do
105
+ find_or_build_and_memoize_details(friendly_model)
106
+ end
107
+ end
108
+
109
+ unless active_record_model.friendly_attributes_configuration
110
+ active_record_model.friendly_attributes_configuration = Configuration.new(active_record_model)
111
+
112
+ active_record_model.class_eval do
113
+ after_save :update_friendly_models
114
+ after_destroy :destroy_friendly_models
115
+ end
116
+ end
117
+
118
+ active_record_model.friendly_attributes_configuration.add(self)
119
+ end
50
120
  end
51
121
  end
@@ -1,33 +1,135 @@
1
1
  module FriendlyAttributes
2
2
  module InstanceMethods
3
- def read_friendly_attribute(column)
4
- details.send(column)
3
+ # Read the value of a Friendly attribute
4
+ #
5
+ # @param [Symbol, String] attr name of the attribute to read
6
+ # @return [Object] value of the read attribute
7
+ def read_friendly_attribute(attr)
8
+ friendly_instance_for_attribute(attr).send(attr)
5
9
  end
6
-
7
- def write_friendly_attribute(column, instance)
8
- details.send(:"#{column}=", instance)
10
+
11
+ # Write the value of a Friendly attribute
12
+ #
13
+ # @param [Symbol, String] attr name of the attribute to set
14
+ # @param [Object] value value to set the attribute to
15
+ def write_friendly_attribute(attr, value)
16
+ friendly_instance_for_attribute(attr).send(:"#{attr}=", value)
9
17
  end
10
-
11
- def details_present?
12
- @details.present?
18
+
19
+ # Returns the Friendly instance corresponding to the specified attribute
20
+ #
21
+ # @param [Symbol, String] attr name of the attribute
22
+ # @return [Class] FriendyAttributes::Base instance
23
+ def friendly_instance_for_attribute(attr)
24
+ klass = friendly_attributes_configuration.model_for_attribute(attr)
25
+ send DetailsDelegator.friendly_model_reader(klass)
13
26
  end
14
-
15
- def update_friendly_details
16
- return unless details_present?
17
- details.send(:"#{details.active_record_key}=", id) unless details.send(details.active_record_key) == id
18
- details.save if details.changed?
27
+
28
+ # Update all associated Friendly instances, if they have been changed.
29
+ # If assigning attributes resulted in new instances being built, they will be created.
30
+ def update_friendly_models
31
+ present_friendly_instances.each do |details|
32
+ details.update_if_changed_with_model(id)
33
+ end
19
34
  end
20
-
21
- def destroy_friendly_details
22
- details.destroy
35
+
36
+ # Destroys all FriendlyAttributes associated with the model. Forces loading and sends :destroy to all associated Friendly models.
37
+ #
38
+ # @return [true, false] result of attempting to destroy the associated FriendlyAttributes
39
+ def destroy_friendly_models
40
+ all_friendly_instances.map(&:destroy).all?
23
41
  end
24
42
 
25
- def friendly_details_build_options
43
+ # Hook provided in order to customize the defaults for building Friendly model instances associated with a certain model.
44
+ # Redefine the method on the FriendlyAttributes::Base subclass to customize.
45
+ #
46
+ # Defaults to an empty Hash.
47
+ #
48
+ # @example We want to specify build options for the UserDetails instances, but not for UserSecondDetails
49
+ # class User < ActiveRecord::Base
50
+ # include FriendlyAttributes
51
+ #
52
+ # friendly_details(UserDetails, Integer => :shoe_size)
53
+ # friendly_details(UserSecondDetails, Integer => :second_int)
54
+ #
55
+ # def friendly_details_build_options(friendly_model)
56
+ # if UserDetails == friendly_model
57
+ # { :shoe_size => 42 }
58
+ # else
59
+ # {}
60
+ # end
61
+ # end
62
+ # end
63
+ #
64
+ # @param [Class] friendly_model FriendlyAttributes::Base subclass for which the build options should be returned
65
+ # @return [Hash] default attributes to be used when building the associated friendly_model
66
+ def friendly_details_build_options(friendly_model = nil)
26
67
  {}
27
68
  end
28
69
 
70
+ # Finds or builds the Friendly instance associated through friendly_model. Result is memoized in an instance variable.
71
+ # @see FriendlyAttributes::Base.find_or_build_by_active_record_id
72
+ # @see FriendlyAttributes::DetailsDelegator.friendly_model_ivar
73
+ #
74
+ # @param [Class] friendly_model FriendlyAttributes::Base subclass
75
+ def find_or_build_and_memoize_details(friendly_model)
76
+ friendly_model_ivar = DetailsDelegator.friendly_model_ivar(friendly_model)
77
+
78
+ val = instance_variable_get(friendly_model_ivar)
79
+ return val if val.present?
80
+
81
+ instance_variable_set(friendly_model_ivar,
82
+ friendly_model.
83
+ find_or_build_by_active_record_id(id, friendly_details_build_options(friendly_model)))
84
+ end
85
+
86
+ # Returns true if the FriendlyAttributes specified instance is loaded.
87
+ #
88
+ # @param [Class, Symbol, String] friendly_model Class or name of the FriendlyAttributes model
89
+ # @return [true, false] is the FriendlyAttributes instance loaded
90
+ def friendly_instance_present?(friendly_model)
91
+ friendly_model_ivar = DetailsDelegator.friendly_model_ivar(friendly_model)
92
+ val = instance_variable_get(friendly_model_ivar)
93
+ val.present?
94
+ end
95
+
96
+ # Returns the associated FriendlyAttributes instance, if it has been loaded. If not loaded, returns nil.
97
+ #
98
+ # @param [Class, Symbol, String] friendly_model Class or name of the FriendlyAttributes model
99
+ # @return [FriendlyAttributes::Base, nil] instance of the FriendlyAttributes or nil
100
+ def friendly_instance_presence(friendly_model)
101
+ friendly_instance_present?(friendly_model) ?
102
+ send(DetailsDelegator.friendly_model_reader(friendly_model)) :
103
+ nil
104
+ end
105
+
106
+ # List of all the FriendlyAttributes::Base instances associated with the model.
107
+ # Forces loading if the details have not been loaded yet.
108
+ #
109
+ # @return [Array<FriendlyAttributes::Base>] FriendlyAttributes instances
110
+ def all_friendly_instances
111
+ friendly_attributes_configuration.friendly_models.map do |friendly_model|
112
+ send(DetailsDelegator.friendly_model_reader(friendly_model))
113
+ end
114
+ end
115
+
116
+ # List of FriendlyAttributes::Base instances that have been loaded.
117
+ # Does not force loading of details not loaded yet.
118
+ #
119
+ # @return [Array<FriendlyAttributes::Base>] FriendlyAttributes instances
120
+ def present_friendly_instances
121
+ friendly_attributes_configuration.friendly_models.map do |friendly_model|
122
+ friendly_instance_presence(friendly_model)
123
+ end.compact
124
+ end
125
+
126
+ # Returns if the record has been changed and should be saved, taking into account any FriendlyAttributes.
127
+ #
128
+ # Overloads ActiveRecord::Base#changed?
129
+ #
130
+ # @return [true, false]
29
131
  def changed?
30
- super || (details_present? && details.changed?)
132
+ super || present_friendly_instances.any?(&:changed?)
31
133
  end
32
134
  end
33
135
  end
@@ -5,6 +5,8 @@ module FriendlyAttributes
5
5
  def initialize(example, type, *attributes)
6
6
  @example = example
7
7
  @type = type
8
+ @options = attributes.extract_options!
9
+ @through = @options[:through]
8
10
  @attributes = attributes
9
11
  end
10
12
 
@@ -12,7 +14,7 @@ module FriendlyAttributes
12
14
  @actual = Class === actual ? actual : actual.class
13
15
 
14
16
  result = @actual.ancestors.include?(FriendlyAttributes) && @attributes.all? { |attr|
15
- @actual.friendly_model.attributes.include?(attr) && @actual.friendly_model.attributes[attr].type == @type
17
+ @through.attributes.include?(attr) && @through.attributes[attr].type == @type
16
18
  }
17
19
  end
18
20
 
@@ -29,6 +31,14 @@ module FriendlyAttributes
29
31
  end
30
32
  end
31
33
 
34
+ # RSpec matcher for checking Friendly attributes.
35
+ # Passes if the model has the specified FriendlyAttributes associated with it.
36
+ #
37
+ # @example Using the matcher
38
+ # it { should have_friendly_attributes(String, :ssn, :work_email, :through => UserDetails) }
39
+ # it { should have_friendly_attributes(Friendly::Boolean, :is_active, :through => CompanyDetails) }
40
+ #
41
+ # @return [HaveFriendlyAttribute] matcher
32
42
  def have_friendly_attributes(*args)
33
43
  HaveFriendlyAttribute.new(self, *args)
34
44
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly-attributes
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
5
- prerelease: false
4
+ hash: 3
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
9
- - 1
10
- version: 0.6.1
8
+ - 7
9
+ - 0
10
+ version: 0.7.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Istvan Hoka
@@ -15,12 +15,10 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-13 00:00:00 +02:00
18
+ date: 2011-02-20 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- prerelease: false
23
- type: :runtime
24
22
  name: activerecord
25
23
  version_requirements: &id001 !ruby/object:Gem::Requirement
26
24
  none: false
@@ -33,26 +31,24 @@ dependencies:
33
31
  - 3
34
32
  - 5
35
33
  version: 2.3.5
36
- requirement: *id001
37
- - !ruby/object:Gem::Dependency
38
34
  prerelease: false
39
35
  type: :runtime
36
+ requirement: *id001
37
+ - !ruby/object:Gem::Dependency
40
38
  name: friendly
41
39
  version_requirements: &id002 !ruby/object:Gem::Requirement
42
40
  none: false
43
41
  requirements:
44
- - - ~>
42
+ - - ">="
45
43
  - !ruby/object:Gem::Version
46
- hash: 7
44
+ hash: 3
47
45
  segments:
48
46
  - 0
49
- - 6
50
- - 0
51
- version: 0.6.0
52
- requirement: *id002
53
- - !ruby/object:Gem::Dependency
47
+ version: "0"
54
48
  prerelease: false
55
49
  type: :runtime
50
+ requirement: *id002
51
+ - !ruby/object:Gem::Dependency
56
52
  name: yajl-ruby
57
53
  version_requirements: &id003 !ruby/object:Gem::Requirement
58
54
  none: false
@@ -65,10 +61,10 @@ dependencies:
65
61
  - 7
66
62
  - 7
67
63
  version: 0.7.7
68
- requirement: *id003
69
- - !ruby/object:Gem::Dependency
70
64
  prerelease: false
71
65
  type: :runtime
66
+ requirement: *id003
67
+ - !ruby/object:Gem::Dependency
72
68
  name: memcached
73
69
  version_requirements: &id004 !ruby/object:Gem::Requirement
74
70
  none: false
@@ -81,12 +77,26 @@ dependencies:
81
77
  - 20
82
78
  - 1
83
79
  version: 0.20.1
80
+ prerelease: false
81
+ type: :runtime
84
82
  requirement: *id004
85
83
  - !ruby/object:Gem::Dependency
84
+ name: ruby-debug
85
+ version_requirements: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
86
94
  prerelease: false
87
- type: :development
95
+ type: :runtime
96
+ requirement: *id005
97
+ - !ruby/object:Gem::Dependency
88
98
  name: mysql
89
- version_requirements: &id005 !ruby/object:Gem::Requirement
99
+ version_requirements: &id006 !ruby/object:Gem::Requirement
90
100
  none: false
91
101
  requirements:
92
102
  - - ~>
@@ -97,28 +107,28 @@ dependencies:
97
107
  - 8
98
108
  - 1
99
109
  version: 2.8.1
100
- requirement: *id005
101
- - !ruby/object:Gem::Dependency
102
110
  prerelease: false
103
111
  type: :development
112
+ requirement: *id006
113
+ - !ruby/object:Gem::Dependency
104
114
  name: rspec
105
- version_requirements: &id006 !ruby/object:Gem::Requirement
115
+ version_requirements: &id007 !ruby/object:Gem::Requirement
106
116
  none: false
107
117
  requirements:
108
118
  - - ~>
109
119
  - !ruby/object:Gem::Version
110
- hash: 11
120
+ hash: 27
111
121
  segments:
112
122
  - 2
113
- - 1
123
+ - 5
114
124
  - 0
115
- version: 2.1.0
116
- requirement: *id006
117
- - !ruby/object:Gem::Dependency
125
+ version: 2.5.0
118
126
  prerelease: false
119
127
  type: :development
128
+ requirement: *id007
129
+ - !ruby/object:Gem::Dependency
120
130
  name: bundler
121
- version_requirements: &id007 !ruby/object:Gem::Requirement
131
+ version_requirements: &id008 !ruby/object:Gem::Requirement
122
132
  none: false
123
133
  requirements:
124
134
  - - ~>
@@ -129,12 +139,12 @@ dependencies:
129
139
  - 0
130
140
  - 0
131
141
  version: 1.0.0
132
- requirement: *id007
133
- - !ruby/object:Gem::Dependency
134
142
  prerelease: false
135
143
  type: :development
144
+ requirement: *id008
145
+ - !ruby/object:Gem::Dependency
136
146
  name: jeweler
137
- version_requirements: &id008 !ruby/object:Gem::Requirement
147
+ version_requirements: &id009 !ruby/object:Gem::Requirement
138
148
  none: false
139
149
  requirements:
140
150
  - - ~>
@@ -145,12 +155,12 @@ dependencies:
145
155
  - 5
146
156
  - 1
147
157
  version: 1.5.1
148
- requirement: *id008
149
- - !ruby/object:Gem::Dependency
150
158
  prerelease: false
151
159
  type: :development
160
+ requirement: *id009
161
+ - !ruby/object:Gem::Dependency
152
162
  name: rcov
153
- version_requirements: &id009 !ruby/object:Gem::Requirement
163
+ version_requirements: &id010 !ruby/object:Gem::Requirement
154
164
  none: false
155
165
  requirements:
156
166
  - - ">="
@@ -159,12 +169,12 @@ dependencies:
159
169
  segments:
160
170
  - 0
161
171
  version: "0"
162
- requirement: *id009
163
- - !ruby/object:Gem::Dependency
164
172
  prerelease: false
165
173
  type: :development
174
+ requirement: *id010
175
+ - !ruby/object:Gem::Dependency
166
176
  name: database_cleaner
167
- version_requirements: &id010 !ruby/object:Gem::Requirement
177
+ version_requirements: &id011 !ruby/object:Gem::Requirement
168
178
  none: false
169
179
  requirements:
170
180
  - - ~>
@@ -175,12 +185,68 @@ dependencies:
175
185
  - 5
176
186
  - 0
177
187
  version: 0.5.0
178
- requirement: *id010
188
+ prerelease: false
189
+ type: :development
190
+ requirement: *id011
179
191
  - !ruby/object:Gem::Dependency
192
+ name: autotest
193
+ version_requirements: &id012 !ruby/object:Gem::Requirement
194
+ none: false
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ hash: 3
199
+ segments:
200
+ - 0
201
+ version: "0"
180
202
  prerelease: false
181
- type: :runtime
203
+ type: :development
204
+ requirement: *id012
205
+ - !ruby/object:Gem::Dependency
206
+ name: autotest-fsevent
207
+ version_requirements: &id013 !ruby/object:Gem::Requirement
208
+ none: false
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ hash: 3
213
+ segments:
214
+ - 0
215
+ version: "0"
216
+ prerelease: false
217
+ type: :development
218
+ requirement: *id013
219
+ - !ruby/object:Gem::Dependency
220
+ name: autotest-growl
221
+ version_requirements: &id014 !ruby/object:Gem::Requirement
222
+ none: false
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ hash: 3
227
+ segments:
228
+ - 0
229
+ version: "0"
230
+ prerelease: false
231
+ type: :development
232
+ requirement: *id014
233
+ - !ruby/object:Gem::Dependency
234
+ name: syntax
235
+ version_requirements: &id015 !ruby/object:Gem::Requirement
236
+ none: false
237
+ requirements:
238
+ - - ">="
239
+ - !ruby/object:Gem::Version
240
+ hash: 3
241
+ segments:
242
+ - 0
243
+ version: "0"
244
+ prerelease: false
245
+ type: :development
246
+ requirement: *id015
247
+ - !ruby/object:Gem::Dependency
182
248
  name: activerecord
183
- version_requirements: &id011 !ruby/object:Gem::Requirement
249
+ version_requirements: &id016 !ruby/object:Gem::Requirement
184
250
  none: false
185
251
  requirements:
186
252
  - - ~>
@@ -191,12 +257,12 @@ dependencies:
191
257
  - 3
192
258
  - 5
193
259
  version: 2.3.5
194
- requirement: *id011
195
- - !ruby/object:Gem::Dependency
196
260
  prerelease: false
197
261
  type: :runtime
262
+ requirement: *id016
263
+ - !ruby/object:Gem::Dependency
198
264
  name: friendly
199
- version_requirements: &id012 !ruby/object:Gem::Requirement
265
+ version_requirements: &id017 !ruby/object:Gem::Requirement
200
266
  none: false
201
267
  requirements:
202
268
  - - ~>
@@ -207,12 +273,12 @@ dependencies:
207
273
  - 6
208
274
  - 0
209
275
  version: 0.6.0
210
- requirement: *id012
211
- - !ruby/object:Gem::Dependency
212
276
  prerelease: false
213
277
  type: :runtime
278
+ requirement: *id017
279
+ - !ruby/object:Gem::Dependency
214
280
  name: yajl-ruby
215
- version_requirements: &id013 !ruby/object:Gem::Requirement
281
+ version_requirements: &id018 !ruby/object:Gem::Requirement
216
282
  none: false
217
283
  requirements:
218
284
  - - ~>
@@ -223,12 +289,12 @@ dependencies:
223
289
  - 7
224
290
  - 7
225
291
  version: 0.7.7
226
- requirement: *id013
227
- - !ruby/object:Gem::Dependency
228
292
  prerelease: false
229
293
  type: :runtime
294
+ requirement: *id018
295
+ - !ruby/object:Gem::Dependency
230
296
  name: memcached
231
- version_requirements: &id014 !ruby/object:Gem::Requirement
297
+ version_requirements: &id019 !ruby/object:Gem::Requirement
232
298
  none: false
233
299
  requirements:
234
300
  - - ~>
@@ -239,12 +305,12 @@ dependencies:
239
305
  - 20
240
306
  - 1
241
307
  version: 0.20.1
242
- requirement: *id014
243
- - !ruby/object:Gem::Dependency
244
308
  prerelease: false
245
- type: :development
309
+ type: :runtime
310
+ requirement: *id019
311
+ - !ruby/object:Gem::Dependency
246
312
  name: mysql
247
- version_requirements: &id015 !ruby/object:Gem::Requirement
313
+ version_requirements: &id020 !ruby/object:Gem::Requirement
248
314
  none: false
249
315
  requirements:
250
316
  - - ~>
@@ -255,12 +321,12 @@ dependencies:
255
321
  - 8
256
322
  - 1
257
323
  version: 2.8.1
258
- requirement: *id015
259
- - !ruby/object:Gem::Dependency
260
324
  prerelease: false
261
325
  type: :development
326
+ requirement: *id020
327
+ - !ruby/object:Gem::Dependency
262
328
  name: rspec
263
- version_requirements: &id016 !ruby/object:Gem::Requirement
329
+ version_requirements: &id021 !ruby/object:Gem::Requirement
264
330
  none: false
265
331
  requirements:
266
332
  - - ~>
@@ -271,12 +337,12 @@ dependencies:
271
337
  - 1
272
338
  - 0
273
339
  version: 2.1.0
274
- requirement: *id016
275
- - !ruby/object:Gem::Dependency
276
340
  prerelease: false
277
341
  type: :development
342
+ requirement: *id021
343
+ - !ruby/object:Gem::Dependency
278
344
  name: bundler
279
- version_requirements: &id017 !ruby/object:Gem::Requirement
345
+ version_requirements: &id022 !ruby/object:Gem::Requirement
280
346
  none: false
281
347
  requirements:
282
348
  - - ~>
@@ -287,12 +353,12 @@ dependencies:
287
353
  - 0
288
354
  - 0
289
355
  version: 1.0.0
290
- requirement: *id017
291
- - !ruby/object:Gem::Dependency
292
356
  prerelease: false
293
357
  type: :development
358
+ requirement: *id022
359
+ - !ruby/object:Gem::Dependency
294
360
  name: jeweler
295
- version_requirements: &id018 !ruby/object:Gem::Requirement
361
+ version_requirements: &id023 !ruby/object:Gem::Requirement
296
362
  none: false
297
363
  requirements:
298
364
  - - ~>
@@ -303,12 +369,12 @@ dependencies:
303
369
  - 5
304
370
  - 1
305
371
  version: 1.5.1
306
- requirement: *id018
307
- - !ruby/object:Gem::Dependency
308
372
  prerelease: false
309
373
  type: :development
374
+ requirement: *id023
375
+ - !ruby/object:Gem::Dependency
310
376
  name: rcov
311
- version_requirements: &id019 !ruby/object:Gem::Requirement
377
+ version_requirements: &id024 !ruby/object:Gem::Requirement
312
378
  none: false
313
379
  requirements:
314
380
  - - ">="
@@ -317,12 +383,12 @@ dependencies:
317
383
  segments:
318
384
  - 0
319
385
  version: "0"
320
- requirement: *id019
321
- - !ruby/object:Gem::Dependency
322
386
  prerelease: false
323
387
  type: :development
388
+ requirement: *id024
389
+ - !ruby/object:Gem::Dependency
324
390
  name: database_cleaner
325
- version_requirements: &id020 !ruby/object:Gem::Requirement
391
+ version_requirements: &id025 !ruby/object:Gem::Requirement
326
392
  none: false
327
393
  requirements:
328
394
  - - ~>
@@ -333,7 +399,9 @@ dependencies:
333
399
  - 5
334
400
  - 0
335
401
  version: 0.5.0
336
- requirement: *id020
402
+ prerelease: false
403
+ type: :development
404
+ requirement: *id025
337
405
  description: Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.
338
406
  email: istvan.hoka@gmail.com
339
407
  executables: []
@@ -342,18 +410,19 @@ extensions: []
342
410
 
343
411
  extra_rdoc_files:
344
412
  - LICENSE.txt
345
- - README.rdoc
413
+ - README.md
346
414
  files:
347
415
  - CHANGELOG.md
348
416
  - LICENSE.txt
349
- - README.rdoc
350
417
  - lib/friendly-attributes.rb
351
418
  - lib/friendly_attributes.rb
419
+ - lib/friendly_attributes/base.rb
352
420
  - lib/friendly_attributes/class_methods.rb
353
- - lib/friendly_attributes/details.rb
421
+ - lib/friendly_attributes/configuration.rb
354
422
  - lib/friendly_attributes/details_delegator.rb
355
423
  - lib/friendly_attributes/instance_methods.rb
356
424
  - lib/friendly_attributes/test/matchers.rb
425
+ - README.md
357
426
  has_rdoc: true
358
427
  homepage: http://github.com/ihoka/friendly-attributes
359
428
  licenses:
@@ -384,7 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
384
453
  requirements: []
385
454
 
386
455
  rubyforge_project:
387
- rubygems_version: 1.3.7
456
+ rubygems_version: 1.5.0
388
457
  signing_key:
389
458
  specification_version: 3
390
459
  summary: Extend ActiveRecord models using Friendly ORM delegate models
data/README.rdoc DELETED
@@ -1,21 +0,0 @@
1
- = friendly-attributes
2
-
3
- Pattern to add fields to ActiveRecord models, using an associated document, without needing schema migrations.
4
-
5
- Uses http://friendlyorm.com/
6
-
7
- == Contributing to friendly-attributes
8
-
9
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
10
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
11
- * Fork the project
12
- * Start a feature/bugfix branch
13
- * Commit and push until you are happy with your contribution
14
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
15
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
16
-
17
- == Copyright
18
-
19
- Copyright (c) 2010 Istvan Hoka. See LICENSE.txt for
20
- further details.
21
-
@@ -1,17 +0,0 @@
1
- module FriendlyAttributes
2
- class Details
3
- class << self
4
- def find_or_build_by_active_record_id(active_record_id, options={})
5
- active_record_id && first(active_record_key => active_record_id) || new(options.merge(active_record_key => active_record_id))
6
- end
7
- end
8
-
9
- def attributes
10
- {}.tap do |attributes|
11
- self.class.attributes.keys.each do |attr|
12
- attributes[attr] = self.send(attr)
13
- end
14
- end
15
- end
16
- end
17
- end