custom_attributes 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ # CustomAttributes
2
+
3
+ require 'custom_attributes/custom_attribute'
4
+ require 'custom_attributes/custom_attribute_value'
5
+ require 'custom_attributes/controller'
6
+ require 'custom_attributes/has_custom_attributes'
7
+ require 'custom_attributes/version'
8
+
9
+ module CustomAttributes
10
+ class << self
11
+ attr_accessor :scope
12
+ attr_accessor :scope_method
13
+ end
14
+ end
15
+
16
+ ActiveSupport.on_load(:active_record) do
17
+ include CustomAttributes::Model
18
+ end
19
+
20
+ ActiveSupport.on_load(:action_controller) do
21
+ include CustomAttributes::Controller
22
+ end
@@ -0,0 +1,20 @@
1
+ module CustomAttributes
2
+
3
+ module Controller
4
+ def self.included(base)
5
+ base.before_filter :set_custom_attribute_scope
6
+ end
7
+
8
+ # override me
9
+ def custom_attribute_scope
10
+ if method_name = ::CustomAttributes.scope_method
11
+ self.__send__(method_name)
12
+ end
13
+ end
14
+
15
+ def set_custom_attribute_scope
16
+ ::CustomAttributes.scope = custom_attribute_scope
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,11 @@
1
+ class ::CustomAttribute < ActiveRecord::Base
2
+ belongs_to :scope, :polymorphic => true
3
+ has_many :custom_attribute_values, :dependent => :destroy
4
+
5
+ def owner
6
+ attributes[:owner].try(:constantize)
7
+ end
8
+
9
+ validates_presence_of :name
10
+ validates_uniqueness_of :name, :scope => [:scope_type, :scope_id]
11
+ end
@@ -0,0 +1,6 @@
1
+ class ::CustomAttributeValue < ActiveRecord::Base
2
+ belongs_to :custom_attribute
3
+
4
+ validates_presence_of :value
5
+ validates_associated :custom_attribute
6
+ end
@@ -0,0 +1,136 @@
1
+ module CustomAttributes
2
+ module Model
3
+ module ClassMethods
4
+ def has_custom_attributes
5
+ self.__send__ :include, CustomAttributes::HasCustomAttributes
6
+ end
7
+ end
8
+
9
+ def self.included(receiver)
10
+ receiver.extend ClassMethods
11
+ end
12
+ end
13
+
14
+ module HasCustomAttributes
15
+ module ClassMethods
16
+ def custom_attributes
17
+ @custom_attributes ||= {}
18
+ @custom_attributes[::CustomAttributes.scope || :no_scope] ||= begin
19
+ arel = ::CustomAttribute.where(:owner => self.to_s)
20
+ if scope = ::CustomAttributes.scope
21
+ arel = arel.where(:scope_type => scope.class.to_s, :scope_id => scope.id)
22
+ else
23
+ arel = arel.where('scope_type IS NULL AND scope_id IS NULL')
24
+ end
25
+ arel
26
+ end
27
+ end
28
+
29
+ def add_custom_attribute(value_or_array)
30
+ case value_or_array
31
+ when ::CustomAttribute
32
+ value_or_array.owner = self.to_s
33
+ value_or_array.save
34
+ when Array
35
+ opts = {:scope => ::CustomAttributes.scope}
36
+ opts.merge!(value_or_array.pop) if value_or_array.last.is_a?(Hash)
37
+ value_or_array.each do |name|
38
+ add_custom_attribute ::CustomAttribute.new(opts.merge(:name => name))
39
+ end
40
+ when String, Symbol
41
+ add_custom_attribute [value_or_array]
42
+ else
43
+ raise "Uexpected values for custom_attributes <<: #{value_or_array.inspect}"
44
+ end
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+ def [](name)
50
+ custom_attributes[name] || super
51
+ end
52
+
53
+ def []=(name, value)
54
+ if custom_attribute_names.include?(name)
55
+ custom_attributes[name] = value
56
+ else
57
+ super
58
+ end
59
+ end
60
+
61
+ def attributes=(hash)
62
+ custom_attribute_names.each do |name|
63
+ if hash.keys.include?(name)
64
+ custom_attributes[name] = hash.delete(name)
65
+ end
66
+ end
67
+ end
68
+
69
+ def attributes
70
+ super.merge custom_attributes
71
+ end
72
+
73
+ def custom_attributes
74
+ @custom_attributes ||= {}
75
+ @custom_attributes[::CustomAttributes.scope || :no_scope] ||= begin
76
+ all_attributes = self.class.custom_attributes
77
+ all_attributes.inject({}) do |attribs, custom_attrib|
78
+ # FIXME: should NOT make a separate request to the db for each value
79
+ attribs[custom_attrib.name] = read_custom_attribute(custom_attrib.name)
80
+ attribs
81
+ end
82
+ end
83
+ end
84
+
85
+ def custom_attribute_names
86
+ self.class.custom_attributes.map(&:name)
87
+ end
88
+
89
+ def method_missing(meth, *args, &blk)
90
+ name = meth.to_s.gsub(/\=$/, '')
91
+ if custom_attribute_names.include?(name)
92
+ if meth =~ /\=$/
93
+ self[name] = args.first
94
+ else
95
+ self[name] || read_custom_attribute(name)
96
+ end
97
+ else
98
+ super
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def attribute_values(name)
105
+ custom_attribute = self.class.custom_attributes.where(:name => name).first
106
+ custom_attribute.custom_attribute_values
107
+ end
108
+
109
+ def read_custom_attribute(name)
110
+ attribute_values(name).where(:owner_id => self.id).first.try(:value)
111
+ end
112
+
113
+ def write_custom_attribute(name, value)
114
+ values = attribute_values(name)
115
+ existing_value = values.where(:owner_id => self.id).first
116
+ if existing_value
117
+ existing_value.update_attribute(:value, value)
118
+ else
119
+ values.create(:owner_id => self.id, :value => value)
120
+ end
121
+ end
122
+
123
+ def write_custom_attributes_from_hash
124
+ custom_attributes.each do |name, value|
125
+ write_custom_attribute(name, value)
126
+ end
127
+ end
128
+ end
129
+
130
+ def self.included(receiver)
131
+ receiver.extend ClassMethods
132
+ receiver.__send__ :include, InstanceMethods
133
+ receiver.after_save :write_custom_attributes_from_hash
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,9 @@
1
+ module CustomAttributes
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ BUILD = 0
6
+
7
+ STRING = [MAJOR, MINOR, BUILD].join('.')
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record/migration'
4
+
5
+ module CustomAttributes
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+ extend ActiveRecord::Generators::Migration
9
+
10
+ source_root File.expand_path('../templates', __FILE__)
11
+
12
+ desc 'Generates (but does not run) a migration to add the custom_attributes table.'
13
+
14
+ def copy_files
15
+ template 'custom_attributes.rb', 'config/initializers/custom_attributes.rb'
16
+ end
17
+
18
+ def create_migration_file
19
+ migration_template 'create_custom_attributes.rb', 'db/migrate/create_custom_attributes.rb'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ class CreateCustomAttributes < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :custom_attributes do |t|
4
+ t.string :name
5
+ t.string :owner
6
+ t.string :scope_type
7
+ t.string :scope_id
8
+ t.timestamps
9
+ end
10
+ add_index :custom_attributes, [:scope_type, :scope_id]
11
+
12
+ create_table :custom_attribute_values do |t|
13
+ t.integer :custom_attribute_id
14
+ t.string :owner_id
15
+ t.string :value
16
+ t.timestamps
17
+ end
18
+ add_index :custom_attribute_values, :custom_attribute_id
19
+ end
20
+
21
+ def self.down
22
+ remove_index :custom_attributes, [:item_type, :item_id]
23
+ drop_table :custom_attributes
24
+ remove_index :custom_attribute_values, [:custom_attribute_id]
25
+ drop_table :custom_attribute_values
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ # Add scope by defining which controller method returns it:
2
+ #
3
+ # ::CustomAttributes.scope_method = :current_user
4
+ #
5
+ # Or set the scope object directly:
6
+ #
7
+ # ::CustomAttributes.scope = User.first
8
+ #
9
+ # Or override the controller method that returns the scope:
10
+ #
11
+ # class ApplicationController < ActionController::Base
12
+ # def custom_attribute_scope
13
+ # # scope determination
14
+ # if current_user.can?(:customize, resource)
15
+ # current_user
16
+ # end
17
+ # end
18
+ # end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class CustomAttributesTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_support'
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: custom_attributes
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - "Ren\xC3\xA9 F\xC3\xB6hring"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-18 00:00:00 +02:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: "3"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: CustomAttributes allows you to add custom attributes to ActiveRecord objects, optionally scoped by another model (e.g. users).
28
+ email: rf@bamaru.de
29
+ executables: []
30
+
31
+ extensions: []
32
+
33
+ extra_rdoc_files: []
34
+
35
+ files:
36
+ - lib/custom_attributes/controller.rb
37
+ - lib/custom_attributes/custom_attribute.rb
38
+ - lib/custom_attributes/custom_attribute_value.rb
39
+ - lib/custom_attributes/has_custom_attributes.rb
40
+ - lib/custom_attributes/version.rb
41
+ - lib/custom_attributes.rb
42
+ - lib/generators/custom_attributes/install_generator.rb
43
+ - lib/generators/custom_attributes/templates/create_custom_attributes.rb
44
+ - lib/generators/custom_attributes/templates/custom_attributes.rb
45
+ - test/custom_attributes_test.rb
46
+ - test/test_helper.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/rrrene/custom_attributes
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ requirements:
69
+ - none
70
+ rubyforge_project:
71
+ rubygems_version: 1.6.2
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: CustomAttributes allows you to add custom attributes to ActiveRecord objects, optionally scoped by another model (e.g. users).
75
+ test_files: []
76
+