eav_hashes 1.0.1 → 1.0.2

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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -20
  3. data/README.md +147 -147
  4. data/Rakefile +30 -30
  5. data/lib/eav_hashes/activerecord_extension.rb +37 -37
  6. data/lib/eav_hashes/eav_entry.rb +128 -128
  7. data/lib/eav_hashes/eav_hash.rb +167 -167
  8. data/lib/eav_hashes/util.rb +122 -122
  9. data/lib/eav_hashes/version.rb +5 -5
  10. data/lib/eav_hashes.rb +8 -8
  11. data/lib/generators/eav_migration/USAGE +26 -26
  12. data/lib/generators/eav_migration/eav_migration.rb +35 -35
  13. data/lib/generators/eav_migration/templates/eav_migration.erb +15 -15
  14. data/spec/dummy/README.rdoc +261 -261
  15. data/spec/dummy/Rakefile +7 -7
  16. data/spec/dummy/app/assets/javascripts/application.js +15 -15
  17. data/spec/dummy/app/assets/stylesheets/application.css +13 -13
  18. data/spec/dummy/app/controllers/application_controller.rb +3 -3
  19. data/spec/dummy/app/helpers/application_helper.rb +2 -2
  20. data/spec/dummy/app/models/custom_test_object.rb +6 -6
  21. data/spec/dummy/app/models/product.rb +3 -4
  22. data/spec/dummy/app/views/layouts/application.html.erb +14 -14
  23. data/spec/dummy/config/application.rb +62 -68
  24. data/spec/dummy/config/boot.rb +9 -9
  25. data/spec/dummy/config/database.yml +25 -25
  26. data/spec/dummy/config/environment.rb +5 -5
  27. data/spec/dummy/config/environments/development.rb +34 -37
  28. data/spec/dummy/config/environments/production.rb +70 -67
  29. data/spec/dummy/config/environments/test.rb +34 -37
  30. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -7
  31. data/spec/dummy/config/initializers/inflections.rb +15 -15
  32. data/spec/dummy/config/initializers/mime_types.rb +5 -5
  33. data/spec/dummy/config/initializers/secret_token.rb +7 -7
  34. data/spec/dummy/config/initializers/session_store.rb +8 -8
  35. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -14
  36. data/spec/dummy/config/locales/en.yml +5 -5
  37. data/spec/dummy/config/routes.rb +58 -58
  38. data/spec/dummy/config.ru +4 -4
  39. data/spec/dummy/db/migrate/20121206133059_create_products.rb +9 -9
  40. data/spec/dummy/db/migrate/20121210055854_create_product_tech_specs.rb +15 -15
  41. data/spec/dummy/db/seeds.rb +30 -30
  42. data/spec/dummy/public/404.html +26 -26
  43. data/spec/dummy/public/422.html +26 -26
  44. data/spec/dummy/public/500.html +25 -25
  45. data/spec/dummy/script/rails +6 -6
  46. data/spec/lib/eav_hashes/eav_hash_spec.rb +147 -137
  47. data/spec/lib/generators/eav_migration_spec.rb +60 -60
  48. data/spec/spec_helper.rb +24 -24
  49. metadata +25 -44
  50. data/spec/dummy/db/development.sqlite3 +0 -0
  51. data/spec/dummy/db/schema.rb +0 -35
  52. data/spec/dummy/db/test.sqlite3 +0 -0
  53. data/spec/dummy/log/ENV=test.log +0 -1
  54. data/spec/dummy/log/development.log +0 -46
  55. data/spec/dummy/log/test.log +0 -4623
@@ -1,129 +1,129 @@
1
- module ActiveRecord
2
- module EavHashes
3
- # Used instead of nil when a nil value is assigned
4
- # (otherwise, the value will try to deserialize itself and
5
- # that would break everything horrifically)
6
- class NilPlaceholder; end
7
-
8
- # Represent an EAV row. This class should NOT be used directly, instead it should be inherited from
9
- # by the class generated by eav_hash_for.
10
- class EavEntry < ActiveRecord::Base
11
- # prevent activerecord from thinking we're trying to do STI
12
- self.abstract_class = true
13
-
14
- # Tell ActiveRecord to convert the value to its DB storable format
15
- before_save :serialize_value
16
-
17
- # Let the key be assignable only once on creation
18
- attr_readonly :entry_key
19
-
20
- # Contains the values the value_type column should have based on the type of the value being stored
21
- SUPPORTED_TYPES = {
22
- :String => 0,
23
- :Symbol => 1,
24
- :Integer => 2,
25
- :Fixnum => 2,
26
- :Bignum => 2,
27
- :Float => 3,
28
- :Complex => 4,
29
- :Rational => 5,
30
- :Boolean => 6, # For code readability
31
- :TrueClass => 6,
32
- :FalseClass => 6,
33
- :Object => 7 # anything else (including Hashes, Arrays) will be serialized to yaml and saved as Object
34
- }
35
-
36
- # Does some sanity checks.
37
- def after_initialize
38
- raise "key should be a string or symbol!" unless key.is_a? String or key.is_a? Symbol
39
- raise "value should not be empty!" if @value.is_a? String and value.empty?
40
- raise "value should not be nil!" if @value.nil?
41
- end
42
-
43
- # Gets the EAV row's value
44
- def value
45
- return nil if @value.is_a? NilPlaceholder
46
- @value.nil? ? deserialize_value : @value
47
- end
48
-
49
- # Sets the EAV row's value
50
- # @param [Object] val the value
51
- def value= (val)
52
- @value = (val.nil? ? NilPlaceholder.new : val)
53
- end
54
-
55
-
56
- def key
57
- k = read_attribute :entry_key
58
- (read_attribute :symbol_key) ? k.to_sym : k
59
- end
60
-
61
- # Raises an error if you try changing the key (unless no key is set)
62
- def key= (val)
63
- raise "Keys are immutable!" if read_attribute(:entry_key)
64
- raise "Key must be a string!" unless val.is_a?(String) or val.is_a?(Symbol)
65
- write_attribute :entry_key, val.to_s
66
- write_attribute :symbol_key, (val.is_a? Symbol)
67
- end
68
-
69
- # Gets the value_type column's value for the type of value passed
70
- # @param [Object] val the object whose value_type to determine
71
- def self.get_value_type (val)
72
- return nil if val.nil?
73
- ret = SUPPORTED_TYPES[val.class.name.to_sym]
74
- if ret.nil?
75
- ret = SUPPORTED_TYPES[:Object]
76
- end
77
- ret
78
- end
79
-
80
- private
81
- # Sets the value_type column to the appropriate value based on the value's type
82
- def update_value_type
83
- write_attribute :value_type, EavEntry.get_value_type(@value)
84
- end
85
-
86
- # Converts the value to its database-storable form and tells ActiveRecord that it's been changed (if it has)
87
- def serialize_value
88
- # Returning nil will prevent the row from being saved, to save some time since the EavHash that manages this
89
- # entry will have marked it for deletion.
90
- raise "Tried to save with a nil value!" if @value.nil? or @value.is_a? NilPlaceholder
91
-
92
- update_value_type
93
- if value_type == SUPPORTED_TYPES[:Object]
94
- write_attribute :value, YAML::dump(@value)
95
- else
96
- write_attribute :value, @value.to_s
97
- end
98
-
99
- read_attribute :value
100
- end
101
-
102
- # Converts the value from it's database representation to the type specified in the value_type column.
103
- def deserialize_value
104
- if @value.nil?
105
- @value = read_attribute :value
106
- end
107
-
108
- case value_type
109
- when SUPPORTED_TYPES[:Object] # or Hash, Array, etc.
110
- @value = YAML::load @value
111
- when SUPPORTED_TYPES[:Symbol]
112
- @value = @value.to_sym
113
- when SUPPORTED_TYPES[:Integer] # or Fixnum, Bignum
114
- @value = @value.to_i
115
- when SUPPORTED_TYPES[:Float]
116
- @value = @value.to_f
117
- when SUPPORTED_TYPES[:Complex]
118
- @value = Complex @value
119
- when SUPPORTED_TYPES[:Rational]
120
- @value = Rational @value
121
- when SUPPORTED_TYPES[:Boolean]
122
- @value = (@value == "true")
123
- else
124
- @value
125
- end
126
- end
127
- end
128
- end
1
+ module ActiveRecord
2
+ module EavHashes
3
+ # Used instead of nil when a nil value is assigned
4
+ # (otherwise, the value will try to deserialize itself and
5
+ # that would break everything horrifically)
6
+ class NilPlaceholder; end
7
+
8
+ # Represent an EAV row. This class should NOT be used directly, instead it should be inherited from
9
+ # by the class generated by eav_hash_for.
10
+ class EavEntry < ActiveRecord::Base
11
+ # prevent activerecord from thinking we're trying to do STI
12
+ self.abstract_class = true
13
+
14
+ # Tell ActiveRecord to convert the value to its DB storable format
15
+ before_save :serialize_value
16
+
17
+ # Let the key be assignable only once on creation
18
+ attr_readonly :entry_key
19
+
20
+ # Contains the values the value_type column should have based on the type of the value being stored
21
+ SUPPORTED_TYPES = {
22
+ :String => 0,
23
+ :Symbol => 1,
24
+ :Integer => 2,
25
+ :Fixnum => 2,
26
+ :Bignum => 2,
27
+ :Float => 3,
28
+ :Complex => 4,
29
+ :Rational => 5,
30
+ :Boolean => 6, # For code readability
31
+ :TrueClass => 6,
32
+ :FalseClass => 6,
33
+ :Object => 7 # anything else (including Hashes, Arrays) will be serialized to yaml and saved as Object
34
+ }
35
+
36
+ # Does some sanity checks.
37
+ def after_initialize
38
+ raise "key should be a string or symbol!" unless key.is_a? String or key.is_a? Symbol
39
+ raise "value should not be empty!" if @value.is_a? String and value.empty?
40
+ raise "value should not be nil!" if @value.nil?
41
+ end
42
+
43
+ # Gets the EAV row's value
44
+ def value
45
+ return nil if @value.is_a? NilPlaceholder
46
+ @value.nil? ? deserialize_value : @value
47
+ end
48
+
49
+ # Sets the EAV row's value
50
+ # @param [Object] val the value
51
+ def value= (val)
52
+ @value = (val.nil? ? NilPlaceholder.new : val)
53
+ end
54
+
55
+
56
+ def key
57
+ k = read_attribute :entry_key
58
+ (read_attribute :symbol_key) ? k.to_sym : k
59
+ end
60
+
61
+ # Raises an error if you try changing the key (unless no key is set)
62
+ def key= (val)
63
+ raise "Keys are immutable!" if read_attribute(:entry_key)
64
+ raise "Key must be a string!" unless val.is_a?(String) or val.is_a?(Symbol)
65
+ write_attribute :entry_key, val.to_s
66
+ write_attribute :symbol_key, (val.is_a? Symbol)
67
+ end
68
+
69
+ # Gets the value_type column's value for the type of value passed
70
+ # @param [Object] val the object whose value_type to determine
71
+ def self.get_value_type (val)
72
+ return nil if val.nil?
73
+ ret = SUPPORTED_TYPES[val.class.name.to_sym]
74
+ if ret.nil?
75
+ ret = SUPPORTED_TYPES[:Object]
76
+ end
77
+ ret
78
+ end
79
+
80
+ private
81
+ # Sets the value_type column to the appropriate value based on the value's type
82
+ def update_value_type
83
+ write_attribute :value_type, EavEntry.get_value_type(@value)
84
+ end
85
+
86
+ # Converts the value to its database-storable form and tells ActiveRecord that it's been changed (if it has)
87
+ def serialize_value
88
+ # Returning nil will prevent the row from being saved, to save some time since the EavHash that manages this
89
+ # entry will have marked it for deletion.
90
+ raise "Tried to save with a nil value!" if @value.nil? or @value.is_a? NilPlaceholder
91
+
92
+ update_value_type
93
+ if value_type == SUPPORTED_TYPES[:Object]
94
+ write_attribute :value, YAML::dump(@value)
95
+ else
96
+ write_attribute :value, @value.to_s
97
+ end
98
+
99
+ read_attribute :value
100
+ end
101
+
102
+ # Converts the value from it's database representation to the type specified in the value_type column.
103
+ def deserialize_value
104
+ if @value.nil?
105
+ @value = read_attribute :value
106
+ end
107
+
108
+ case value_type
109
+ when SUPPORTED_TYPES[:Object] # or Hash, Array, etc.
110
+ @value = YAML::load @value
111
+ when SUPPORTED_TYPES[:Symbol]
112
+ @value = @value.to_sym
113
+ when SUPPORTED_TYPES[:Integer] # or Fixnum, Bignum
114
+ @value = @value.to_i
115
+ when SUPPORTED_TYPES[:Float]
116
+ @value = @value.to_f
117
+ when SUPPORTED_TYPES[:Complex]
118
+ @value = Complex @value
119
+ when SUPPORTED_TYPES[:Rational]
120
+ @value = Rational @value
121
+ when SUPPORTED_TYPES[:Boolean]
122
+ @value = (@value == "true")
123
+ else
124
+ @value
125
+ end
126
+ end
127
+ end
128
+ end
129
129
  end
@@ -1,168 +1,168 @@
1
- module ActiveRecord
2
- module EavHashes
3
- # Wraps a bunch of EavEntries and lets you use them like you would a hash
4
- # This class should not be used directly and you should instead let eav_hash_for create one for you
5
- class EavHash
6
- # Creates a new EavHash. You should really let eav_hash_for do this for you...
7
- # @param [ActiveRecord::Base] owner the Model which will own this hash
8
- # @param [Hash] options the options hash which eav_hash generated
9
- def initialize(owner, options)
10
- Util::sanity_check options
11
- @owner = owner
12
- @options = options
13
- end
14
-
15
- # Saves any modified entries and deletes any which have been nil'd to save DB space
16
- def save_entries
17
- # The entries are lazy-loaded, so don't do anything if they haven't been accessed or modified
18
- return unless (@entries and @changes_made)
19
-
20
- @entries.values.each do |entry|
21
- if entry.value.nil?
22
- entry.delete
23
- else
24
- set_entry_owner(entry)
25
- entry.save
26
- end
27
- end
28
- end
29
-
30
- # Gets the value of an EAV attribute
31
- # @param [String, Symbol] key
32
- def [](key)
33
- raise "Key must be a string or a symbol!" unless key.is_a?(String) or key.is_a?(Symbol)
34
- load_entries_if_needed
35
- return @entries[key].value if @entries[key]
36
- nil
37
- end
38
-
39
- # Sets the value of the EAV attribute `key` to `value`
40
- # @param [String, Symbol] key the attribute
41
- # @param [Object] value the value
42
- def []=(key, value)
43
- update_or_create_entry key, value
44
- end
45
-
46
- # I don't know why Ruby hashes don't have a shovel operator, but I will make damn sure that I
47
- # fight the power and stick it to the man by implementing it.
48
- # @param [Hash, EavHash] dirt the dirt to shovel (ba dum, tss)
49
- def <<(dirt)
50
- if dirt.is_a? Hash
51
- dirt.each do |key, value|
52
- update_or_create_entry key, value
53
- end
54
- elsif dirt.is_a? EavHash
55
- dirt.entries.each do |key, entry|
56
- update_or_create_entry key, entry.value
57
- end
58
- else
59
- raise "You can't shovel something that's not a Hash or EavHash here!"
60
- end
61
-
62
- self
63
- end
64
-
65
- # Gets the raw hash containing EavEntries by their keys
66
- def entries
67
- load_entries_if_needed
68
- end
69
-
70
- # Gets the actual values this EavHash contains
71
- def values
72
- load_entries_if_needed
73
-
74
- ret = []
75
- @entries.values.each do |value|
76
- ret << value
77
- end
78
-
79
- ret
80
- end
81
-
82
- # Gets the keys this EavHash manages
83
- def keys
84
- load_entries_if_needed
85
- @entries.keys
86
- end
87
-
88
- # Emulates Hash.each
89
- def each (&block)
90
- as_hash.each block
91
- end
92
-
93
- # Emulates Hash.each_pair (same as each)
94
- def each_pair (&block)
95
- each &block
96
- end
97
-
98
- # Empties the hash by setting all the values to nil
99
- # (without committing them, of course)
100
- def clear
101
- load_entries_if_needed
102
- @entries.each do |_, entry|
103
- entry.value = nil
104
- end
105
- end
106
-
107
- # Returns a hash with each entry key mapped to its actual value,
108
- # not the internal EavEntry
109
- def as_hash
110
- load_entries_if_needed
111
- hsh = {}
112
- @entries.each do |k, entry|
113
- hsh[k] = entry.value
114
- end
115
-
116
- hsh
117
- end
118
-
119
- # Take the crap out of #inspect calls
120
- def inspect
121
- as_hash
122
- end
123
-
124
- private
125
- def update_or_create_entry(key, value)
126
- raise "Key must be a string or a symbol!" unless key.is_a?(String) or key.is_a?(Symbol)
127
- load_entries_if_needed
128
-
129
- @changes_made = true
130
- @owner.updated_at = Time.now
131
-
132
- if @entries[key]
133
- @entries[key].value = value
134
- else
135
- new_entry = @options[:entry_class].new
136
- set_entry_owner(new_entry)
137
- new_entry.key = key
138
- new_entry.value = value
139
-
140
- @entries[key] = new_entry
141
-
142
- value
143
- end
144
- end
145
-
146
- # Since entries are lazy-loaded, this is called just before an operation on an entry happens and
147
- # loads the rows only once per EavHash lifetime.
148
- def load_entries_if_needed
149
- if @entries.nil?
150
- @entries = {}
151
- rows_from_model = @owner.send("#{@options[:entry_assoc_name]}")
152
- rows_from_model.each do |row|
153
- @entries[row.key] = row
154
- end
155
- end
156
-
157
- @entries
158
- end
159
-
160
- # Sets an entry's owner ID. This is called when we save attributes for a model which has just been
161
- # created and not committed to the DB prior to having its EAV hash(es) modified
162
- # @param [EavEntry] entry the entry whose owner to change
163
- def set_entry_owner(entry)
164
- entry.send "#{@options[:parent_assoc_name]}_id=", @owner.id
165
- end
166
- end
167
- end
1
+ module ActiveRecord
2
+ module EavHashes
3
+ # Wraps a bunch of EavEntries and lets you use them like you would a hash
4
+ # This class should not be used directly and you should instead let eav_hash_for create one for you
5
+ class EavHash
6
+ # Creates a new EavHash. You should really let eav_hash_for do this for you...
7
+ # @param [ActiveRecord::Base] owner the Model which will own this hash
8
+ # @param [Hash] options the options hash which eav_hash generated
9
+ def initialize(owner, options)
10
+ Util::sanity_check options
11
+ @owner = owner
12
+ @options = options
13
+ end
14
+
15
+ # Saves any modified entries and deletes any which have been nil'd to save DB space
16
+ def save_entries
17
+ # The entries are lazy-loaded, so don't do anything if they haven't been accessed or modified
18
+ return unless (@entries and @changes_made)
19
+
20
+ @entries.values.each do |entry|
21
+ if entry.value.nil?
22
+ entry.delete
23
+ else
24
+ set_entry_owner(entry)
25
+ entry.save
26
+ end
27
+ end
28
+ end
29
+
30
+ # Gets the value of an EAV attribute
31
+ # @param [String, Symbol] key
32
+ def [](key)
33
+ raise "Key must be a string or a symbol!" unless key.is_a?(String) or key.is_a?(Symbol)
34
+ load_entries_if_needed
35
+ return @entries[key].value if @entries[key]
36
+ nil
37
+ end
38
+
39
+ # Sets the value of the EAV attribute `key` to `value`
40
+ # @param [String, Symbol] key the attribute
41
+ # @param [Object] value the value
42
+ def []=(key, value)
43
+ update_or_create_entry key, value
44
+ end
45
+
46
+ # I don't know why Ruby hashes don't have a shovel operator, but I will make damn sure that I
47
+ # fight the power and stick it to the man by implementing it.
48
+ # @param [Hash, EavHash] dirt the dirt to shovel (ba dum, tss)
49
+ def <<(dirt)
50
+ if dirt.is_a? Hash
51
+ dirt.each do |key, value|
52
+ update_or_create_entry key, value
53
+ end
54
+ elsif dirt.is_a? EavHash
55
+ dirt.entries.each do |key, entry|
56
+ update_or_create_entry key, entry.value
57
+ end
58
+ else
59
+ raise "You can't shovel something that's not a Hash or EavHash here!"
60
+ end
61
+
62
+ self
63
+ end
64
+
65
+ # Gets the raw hash containing EavEntries by their keys
66
+ def entries
67
+ load_entries_if_needed
68
+ end
69
+
70
+ # Gets the actual values this EavHash contains
71
+ def values
72
+ load_entries_if_needed
73
+
74
+ ret = []
75
+ @entries.values.each do |value|
76
+ ret << value
77
+ end
78
+
79
+ ret
80
+ end
81
+
82
+ # Gets the keys this EavHash manages
83
+ def keys
84
+ load_entries_if_needed
85
+ @entries.keys
86
+ end
87
+
88
+ # Emulates Hash.each
89
+ def each (&block)
90
+ as_hash.each block
91
+ end
92
+
93
+ # Emulates Hash.each_pair (same as each)
94
+ def each_pair (&block)
95
+ each &block
96
+ end
97
+
98
+ # Empties the hash by setting all the values to nil
99
+ # (without committing them, of course)
100
+ def clear
101
+ load_entries_if_needed
102
+ @entries.each do |_, entry|
103
+ entry.value = nil
104
+ end
105
+ end
106
+
107
+ # Returns a hash with each entry key mapped to its actual value,
108
+ # not the internal EavEntry
109
+ def as_hash
110
+ load_entries_if_needed
111
+ hsh = {}
112
+ @entries.each do |k, entry|
113
+ hsh[k] = entry.value
114
+ end
115
+
116
+ hsh
117
+ end
118
+
119
+ # Take the crap out of #inspect calls
120
+ def inspect
121
+ as_hash
122
+ end
123
+
124
+ private
125
+ def update_or_create_entry(key, value)
126
+ raise "Key must be a string or a symbol!" unless key.is_a?(String) or key.is_a?(Symbol)
127
+ load_entries_if_needed
128
+
129
+ @changes_made = true
130
+ @owner.updated_at = Time.now
131
+
132
+ if @entries[key]
133
+ @entries[key].value = value
134
+ else
135
+ new_entry = @options[:entry_class].new
136
+ set_entry_owner(new_entry)
137
+ new_entry.key = key
138
+ new_entry.value = value
139
+
140
+ @entries[key] = new_entry
141
+
142
+ value
143
+ end
144
+ end
145
+
146
+ # Since entries are lazy-loaded, this is called just before an operation on an entry happens and
147
+ # loads the rows only once per EavHash lifetime.
148
+ def load_entries_if_needed
149
+ if @entries.nil?
150
+ @entries = {}
151
+ rows_from_model = @owner.send("#{@options[:entry_assoc_name]}")
152
+ rows_from_model.each do |row|
153
+ @entries[row.key] = row
154
+ end
155
+ end
156
+
157
+ @entries
158
+ end
159
+
160
+ # Sets an entry's owner ID. This is called when we save attributes for a model which has just been
161
+ # created and not committed to the DB prior to having its EAV hash(es) modified
162
+ # @param [EavEntry] entry the entry whose owner to change
163
+ def set_entry_owner(entry)
164
+ entry.send "#{@options[:parent_assoc_name]}_id=", @owner.id
165
+ end
166
+ end
167
+ end
168
168
  end