hstore_attribute_support 0.0.3

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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ nbproject/
2
+ .DS_Store
3
+ *.gem
data/README ADDED
@@ -0,0 +1,44 @@
1
+ == hstore_attribute_support
2
+
3
+ A class that has hstore attribute support will get the ability to set up virtual
4
+ attributes stored in hstore columns very easily.
5
+
6
+ Consider a Person class with two hstore columns named "work_details" and
7
+ "address". This class will, upon activation of the hstore attribute support, get
8
+ new class methods: work_details_hstore_accessor and address_hstore_accessor.
9
+
10
+ These methods can be used on class level (like attr_accessor) to set up getter
11
+ and setter methods for virtual attributes stored in these hstore columns.
12
+ Additionally it will allow you to give a type hint and default values along.
13
+ Because of that, new instances will be able to use an "after_initialize"
14
+ callback to set up their hstore'd attributes to the default value which was
15
+ provided, as well as enabling the getter methods to return values typecasted
16
+ (hstore data looses its type and was returned as a string otherwise...)
17
+
18
+ **Example:**
19
+
20
+ # Schema for User:
21
+ # - id: int
22
+ # - name: string
23
+ # - data: hstore
24
+ class User < ActiveRecord::Base
25
+
26
+ has_hstore_columns # activates hstore support and mixes in the new methods
27
+
28
+ # the explicit definition of a boolean attribute admin (stored in hstore
29
+ # column 'data', initialized with default value false) looks like this:
30
+ hstore_attr_accessor :admin, :boolean, false
31
+
32
+ # however for each hstore column there are autogenerated prefixed methods:
33
+ data_hstore_accessor :age, :integer # defaults to nil
34
+ data_hstore_accessor :salary, :float, 1234.56 # with default value 1234.56
35
+
36
+ # it is even possible to define a custom typecast via a lambda
37
+ data_hstore_accessor :icq, lambda{|n| n.blank? ? 'UIN: n/a' : "UIN: \##{n}"}
38
+
39
+ # as accessors get defined, validation and other stuff works out of the box:
40
+ validates :salary,
41
+ :numericality => {:greater_than => 2000},
42
+ :if => Proc.new { |u| u.admin? }
43
+
44
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'hstore_attribute_support'
3
+ s.version = '0.0.3'
4
+ s.date = '2011-11-07'
5
+ s.summary = "Adds AR Attributes support for Postgres hstore columns "
6
+ s.description = "Adds AR Attributes support for Postgres hstore columns "
7
+ s.authors = ["Andreas Schwarzkopf"]
8
+ s.email = 'asmailbox@gmx.de'
9
+ s.homepage = 'http://rubygems.org/gems/hstore_attributes_support'
10
+ s.has_rdoc = true
11
+ s.rdoc_options = ['--charset=UTF-8']
12
+ s.files = `git ls-files`.split("\n")
13
+ s.require_paths = ["lib"]
14
+
15
+ s.add_dependency 'activerecord-postgres-hstore', '>= 0.1.2'
16
+ end
@@ -0,0 +1,134 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'active_support/concern'
4
+
5
+ module HstoreAttributeSupport
6
+
7
+ module Base
8
+
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ def self.has_hstore_columns?
13
+ true
14
+ end
15
+
16
+ class_attribute :hstore_attributes
17
+
18
+ # now we autogenerate convenient hstore_attr_accessor delegates, which can
19
+ # be used to set up this models hstore'd attributes more rails like.
20
+ # A Person class with a data hstore column could setup its name like this:
21
+ #
22
+ # data_hstore_accessor :name, :string, ''
23
+ #
24
+ # Which will set up accessors to the virtual attribute "name", defaults to
25
+ # an empty string and is stored in the data column.
26
+ # These methods are actually only syntactic sugar for the
27
+ # hstore_attr_accessor method.
28
+ self.columns.each do |column|
29
+ next unless column.type == :hstore
30
+ class_eval %Q{
31
+ def self.#{column.name}_hstore_accessor(attribute_name, cast = nil, default = nil)
32
+ return hstore_attr_accessor(attribute_name, "#{column.name}", cast, default)
33
+ end
34
+ }
35
+ end
36
+
37
+ # this will set up an after_initialize callback allowing a new model
38
+ # instance to set up default values for its hstore'd attributes.
39
+ after_initialize do
40
+ # iterate over the class instance variable "hstore_attributes", which
41
+ # contains types & defaults for virtual hstore attributes and apply them
42
+ (self.class.hstore_attributes || {}).each do |attr_name, settings|
43
+ column = settings[:column]
44
+ default = settings[:default]
45
+ hstore_data = self.send(:"#{column}") || {}
46
+ hstore_data = { attr_name.to_s => default }.merge(hstore_data)
47
+ self.send(:"#{column}=", hstore_data)
48
+ end
49
+ end
50
+ end
51
+
52
+ module ClassMethods
53
+
54
+ # this class method allows descendant classes to set up attributes, which
55
+ # are stored in a hstore column. this solves two problems:
56
+ # 1. by providing a cast hint, it will take care of the data type, which
57
+ # is lost by the hstore and was a pure string otherwise
58
+ # 2. it provides an easy way to set up defaults, as rails AR mechanism
59
+ # fail here (no database defaults available for virtual attributes)
60
+ def hstore_attr_accessor(attribute_name, hstore_column, type = nil, default = nil)
61
+ self.hstore_attributes ||= {}
62
+
63
+ self.hstore_attributes[attribute_name.to_s] = {
64
+ :column => hstore_column.to_s,
65
+ :type => type,
66
+ :default => default
67
+ }
68
+
69
+ class_eval %Q{
70
+ def #{attribute_name}
71
+ return read_hstore_attribute("#{attribute_name}")
72
+ end
73
+
74
+ def #{attribute_name}?
75
+ return read_hstore_attribute("#{attribute_name}").present?
76
+ end
77
+
78
+ def #{attribute_name}=(value)
79
+ return write_hstore_attribute("#{attribute_name}", value)
80
+ end
81
+ }
82
+ end
83
+ end
84
+
85
+ module InstanceMethods
86
+
87
+ def read_hstore_attribute(attribute_name)
88
+ ha_column = self.class.hstore_attributes[attribute_name][:column]
89
+ ha_type = self.class.hstore_attributes[attribute_name][:type]
90
+ hstore_data = self.send(:"#{ha_column}").try(:with_indifferent_access)
91
+
92
+ return nil unless hstore_data
93
+
94
+ value = hstore_data[attribute_name]
95
+
96
+ return value unless ha_type
97
+
98
+ case ha_type
99
+ when :integer
100
+ return value.to_i
101
+ when :float
102
+ return value.to_f
103
+ when :decimal
104
+ return value.to_s.to_d
105
+ when :boolean
106
+ return !(value.to_s == '' || value.to_s == 'false')
107
+ when :bool
108
+ return !(value.to_s == '' || value.to_s == 'false')
109
+ when :string
110
+ return value.to_s
111
+ when :datetime
112
+ return value.to_s.to_datetime
113
+ when :date
114
+ return value.to_s.to_date
115
+ else
116
+ end
117
+
118
+ return ha_type.call(value) if ha_type.is_a?(Proc)
119
+
120
+ raise "read_hstore_attribute failed when trying to cast the type \"#{ha_type.inspect}\". Please define the type as one of [:integer, :float, :decimal, :boolean, :string, :datetime, :date] or pass in a lambda with one parameter to perform custom casts."
121
+ end
122
+
123
+ def write_hstore_attribute(attribute_name, value)
124
+ ha_column = self.class.hstore_attributes[attribute_name][:column]
125
+ hstore_data = (self.send(:"#{ha_column}") || {}).with_indifferent_access
126
+ hstore_data = hstore_data.merge({attribute_name => value})
127
+ self.send(:"#{ha_column}=", hstore_data)
128
+ self
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: UTF-8
2
+
3
+ module HstoreAttributeSupport
4
+
5
+ module Bootstrap
6
+ def has_hstore_columns?
7
+ false
8
+ end
9
+
10
+ def has_hstore_columns
11
+ unless self.include? HstoreAttributeSupport::Base
12
+ self.send :include, HstoreAttributeSupport::Base
13
+ end
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,142 @@
1
+
2
+ module Something
3
+ module ClassMethods
4
+ # define an class instance variable - different for each subclass, but the
5
+ # same for each instance of these subclasses...
6
+ # its a hash containing sym.-keys representing the "virtual attribute name"
7
+ # and values which are hashes with :column, :cast, and :default values.
8
+ #
9
+ # :column defines the AR hstore column name where the attribute is stored
10
+ # :type contains a symbol defining the type or a custom proc that converts
11
+ # the value in a user defined way
12
+ # :default is the default value for this attribute
13
+ #
14
+ # An example for a person model with a "work_details" and "address" hstore
15
+ # column could look like this:
16
+ #
17
+ # hstore_attributes => {
18
+ # :employer=>{:column => work_details, :type => :string, :default => ""},
19
+ # :salary =>{:column => work_details, :type => :integer, :default => 0},
20
+ # :street =>{:column => address, :type => :string, :default => ""},
21
+ # :city =>{:column => address, :type => :string, :default => ""},
22
+ # }
23
+ #
24
+ # thus, the model has four virtual attributes which are stored in two
25
+ # differrent hstore columns.
26
+ class << self; attr_accessor :hstore_attributes; end
27
+
28
+ class_eval %Q{
29
+ # this will set up an after_initialize callback allowing a new model
30
+ # instance to set up default values for its hstore'd attributes.
31
+ after_initialize do
32
+ # iterate over the class instance variable "hstore_attributes", which
33
+ # contains types & defaults for virtual hstore attributes and apply them
34
+ (self.class.hstore_attributes || {}).each do |attr_name, settings|
35
+ column = settings[:column]
36
+ default = settings[:default]
37
+ hstore_data = self.send(:"\#{column}") || {}
38
+ hstore_data = { attr_name.to_s => default }.merge(hstore_data)
39
+ self.send(:"\#{column}=", hstore_data)
40
+ end
41
+ end
42
+ }
43
+
44
+ # now we autogenerates convenient hstore_attr_accessor delegates, which can
45
+ # be used to set up this models hstore'd attributes more rails like.
46
+ # A Person class with a data hstore column could setup its name like this:
47
+ #
48
+ # data_hstore_accessor :name, :string, ''
49
+ #
50
+ # Which will set up accessors to the virtual attribute "name", defaults to
51
+ # an empty string and is stored in the data column.
52
+ # These methods are actually only syntactic sugar for the hstore_attr_accessor
53
+ # method.
54
+ columns.each do |column|
55
+ next unless column.type == :hstore
56
+ class_eval %Q{
57
+ def self.#{column.name}_hstore_accessor(attribute_name, cast = nil, default = nil)
58
+ return hstore_attr_accessor(attribute_name, "#{column.name}", cast, default)
59
+ end
60
+ }
61
+ end
62
+
63
+ # this class method allows descendant classes to set up attributes, which are
64
+ # stored in a hstore column. this solves two problems:
65
+ # 1. by providing a cast hint, it will take care of the data type, which is
66
+ # lost by the hstore and was a pure string otherwise
67
+ # 2. it provides an easy way of setting up defaults, as rails AR mechanisms
68
+ # fail here (no database defaults available for virtual attributes)
69
+ def self.hstore_attr_accessor(attribute_name, hstore_column, type = nil, default = nil)
70
+ self.hstore_attributes ||= {}
71
+
72
+ self.hstore_attributes[attribute_name.to_s] = {
73
+ :column => hstore_column.to_s,
74
+ :type => type,
75
+ :default => default
76
+ }
77
+
78
+ class_eval %Q{
79
+ def #{attribute_name}
80
+ return read_hstore_attribute("#{attribute_name}")
81
+ end
82
+
83
+ def #{attribute_name}?
84
+ return read_hstore_attribute("#{attribute_name}").present?
85
+ end
86
+
87
+ def #{attribute_name}=(value)
88
+ return write_hstore_attribute("#{attribute_name}", value)
89
+ end
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ def read_hstore_attribute(attribute_name)
96
+ ha_column = self.class.hstore_attributes[attribute_name][:column]
97
+ ha_type = self.class.hstore_attributes[attribute_name][:type]
98
+ hstore_data = self.send(:"#{ha_column}").try(:with_indifferent_access)
99
+
100
+ return nil unless hstore_data
101
+
102
+ value = hstore_data[attribute_name]
103
+
104
+ return value unless ha_type
105
+
106
+ case ha_type
107
+ when :integer
108
+ return value.to_i
109
+ when :float
110
+ return value.to_f
111
+ when :decimal
112
+ return value.to_s.to_d
113
+ when :boolean
114
+ return !(value.to_s == '' || value.to_s == 'false')
115
+ when :bool
116
+ return !(value.to_s == '' || value.to_s == 'false')
117
+ when :string
118
+ return value.to_s
119
+ when :datetime
120
+ return value.to_s.to_datetime
121
+ when :date
122
+ return value.to_s.to_date
123
+ else
124
+ end
125
+
126
+ return ha_type.call(value) if ha_type.is_a?(Proc)
127
+
128
+ raise "read_hstore_attribute failed when trying to cast the type \"#{ha_type.inspect}\". Please define the type as one of [:integer, :float, :decimal, :boolean, :string, :datetime, :date] or pass in a lambda with one parameter to perform custom casts."
129
+ end
130
+
131
+ def write_hstore_attribute(attribute_name, value)
132
+ ha_column = self.class.hstore_attributes[attribute_name][:column]
133
+ hstore_data = (self.send(:"#{ha_column}") || {}).with_indifferent_access
134
+ hstore_data = hstore_data.merge({attribute_name => value})
135
+ self.send(:"#{ha_column}=", hstore_data)
136
+ end
137
+
138
+ end
139
+
140
+ module InstanceMethods
141
+ end
142
+ end
@@ -0,0 +1,7 @@
1
+ require "hstore_attribute_support/bootstrap"
2
+ require "hstore_attribute_support/base"
3
+
4
+ # Attach ourselves to ActiveRecord
5
+ if defined?(ActiveRecord::Base)
6
+ ActiveRecord::Base.extend HstoreAttributeSupport::Bootstrap
7
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hstore_attribute_support
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andreas Schwarzkopf
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord-postgres-hstore
16
+ requirement: &70277821577800 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70277821577800
25
+ description: ! 'Adds AR Attributes support for Postgres hstore columns '
26
+ email: asmailbox@gmx.de
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - .gitignore
32
+ - README
33
+ - hstore_attribute_support.gemspec
34
+ - lib/hstore_attribute_support.rb
35
+ - lib/hstore_attribute_support/base.rb
36
+ - lib/hstore_attribute_support/bootstrap.rb
37
+ - lib/hstore_attribute_support/snippets.rb
38
+ homepage: http://rubygems.org/gems/hstore_attributes_support
39
+ licenses: []
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.11
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Adds AR Attributes support for Postgres hstore columns
63
+ test_files: []