Module: HashAttributes

Extended by:
ActiveSupport::Concern
Defined in:
lib/hash_attributes/version.rb,
lib/hash_attributes/hash_attributes.rb,
lib/hash_attributes/date_time_serializer.rb

Defined Under Namespace

Modules: ClassMethods Classes: DateTimeSerializer

Constant Summary collapse

VERSION =
"1.0.0"

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_symbol, *args, &block) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/hash_attributes/hash_attributes.rb', line 207

def method_missing(method_symbol, *args, &block)
  attribute_name = self.class.extract_attribute_name(method_symbol)
  if attribute_name.present? &&
     self.class.column_names.include?(attribute_name) == false &&
     self.class.method_defined?(attribute_name) == false

      self.class.define_hash_column_attribute(attribute_name)
      __send__(method_symbol, *args)
  else
    super
  end
end

Instance Method Details

#[](attribute_name) ⇒ Object



112
113
114
# File 'lib/hash_attributes/hash_attributes.rb', line 112

def [](attribute_name)
  read_attribute(attribute_name)
end

#[]=(attribute_name, value) ⇒ Object



116
117
118
# File 'lib/hash_attributes/hash_attributes.rb', line 116

def []=(attribute_name, value)
  write_attribute(attribute_name, value)
end

#assign_attributes(new_attributes) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/hash_attributes/hash_attributes.rb', line 152

def assign_attributes(new_attributes)
  return if new_attributes.blank?
  new_attributes = new_attributes.with_indifferent_access
  __hash_column_attributes__ = new_attributes.except(*self.class.column_names)
  if __hash_column_attributes__.present?
    __hash_column_attributes__ = {self.class.hash_column => read_attribute(self.class.hash_column).merge(__hash_column_attributes__)}
    new_attributes = new_attributes.slice(*self.class.column_names).merge(__hash_column_attributes__)
  end
  super(new_attributes)
end

#attribute_namesObject



128
129
130
# File 'lib/hash_attributes/hash_attributes.rb', line 128

def attribute_names
  ((self.class.column_names - [self.class.hash_column]) + hash_column_attribute_names).sort
end

#attributesObject



132
133
134
# File 'lib/hash_attributes/hash_attributes.rb', line 132

def attributes
  (super || {}).with_indifferent_access.except(self.class.hash_column).merge(hash_column_attributes)
end

#attributes_before_type_castObject



94
95
96
97
98
99
100
101
102
# File 'lib/hash_attributes/hash_attributes.rb', line 94

def attributes_before_type_cast
  self.class.column_names.inject(HashWithIndifferentAccess.new) do |result, attribute_name|
    if attribute_name == self.class.hash_column
      result.merge(read_attribute(attribute_name) || {})
    else
      result.merge(attribute_name => read_attribute(attribute_name))
    end
  end
end

#cache_keyObject



169
170
171
# File 'lib/hash_attributes/hash_attributes.rb', line 169

def cache_key
  "#{self.class.name.underscore.dasherize}-#{read_attribute(self.class.primary_key)}-version-#{Digest::MD5.hexdigest(attributes.inspect)}"
end

#column_for_attribute(attribute_name) ⇒ Object



136
137
138
139
140
141
142
143
# File 'lib/hash_attributes/hash_attributes.rb', line 136

def column_for_attribute(attribute_name)
  attribute_name = attribute_name.to_s
  if is_valid_hash_column_attribute_name?(attribute_name)
    nil
  else
    super(attribute_name)
  end
end

#delete_hash_column_attribute(attribute_name) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/hash_attributes/hash_attributes.rb', line 177

def delete_hash_column_attribute(attribute_name)
  if is_valid_hash_column_attribute_name?(attribute_name)
    __hash_column_value__ = read_attribute(self.class.hash_column)
    __result__ = __hash_column_value__.delete(attribute_name)
    write_attribute(self.class.hash_column, __hash_column_value__)
    self.class.undefine_attribute_method(attribute_name)
    __result__
  else
    nil
  end
end

#has_attribute?(attribute_name) ⇒ Boolean

Returns:

  • (Boolean)


145
146
147
148
# File 'lib/hash_attributes/hash_attributes.rb', line 145

def has_attribute?(attribute_name)
  attribute_name = attribute_name.to_s
  hash_column_attribute_names.include?(attribute_name) || super(attribute_name)
end

#hash_column_attribute_namesObject



124
125
126
# File 'lib/hash_attributes/hash_attributes.rb', line 124

def hash_column_attribute_names
  read_attribute(self.class.hash_column).keys.sort
end

#hash_column_attributesObject



120
121
122
# File 'lib/hash_attributes/hash_attributes.rb', line 120

def hash_column_attributes
  self.class.deserialize_hash_column_attribute(self.class.hash_column, read_attribute(self.class.hash_column))
end

#initialize(*args, &block) ⇒ Object



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/hash_attributes/hash_attributes.rb', line 8

def initialize(*args, &block)
  if args[0].is_a?(Hash)
    args[0] = args[0].with_indifferent_access
    args[0] = args[0].inject(HashWithIndifferentAccess.new(self.class.hash_column => {})) do |result, (attribute_name, value)|
      if self.class.hash_column == attribute_name
        result[self.class.hash_column].merge!(value)
      elsif self.class.column_names.include?(attribute_name)
        result[attribute_name] = value
      else
        result[self.class.hash_column][attribute_name] = value
      end
      result
    end
  end

  if block_given?
    super(*args) do |new_record|
      yield new_record if block_given?
    end
  else
    super(*args)
  end
end

#inspectObject



173
174
175
# File 'lib/hash_attributes/hash_attributes.rb', line 173

def inspect
  "#<#{self.class.name} #{attributes.map{ |k, v| "#{k}: #{v.inspect}" }.join(", ")}>"
end

#is_valid_hash_column_attribute_name?(attribute_name) ⇒ Boolean

Returns:

  • (Boolean)


32
33
34
35
# File 'lib/hash_attributes/hash_attributes.rb', line 32

def is_valid_hash_column_attribute_name?(attribute_name)
  attribute_name = attribute_name.to_s
  self.class.column_names.include?(attribute_name) == false && self.class.is_valid_attribute_name?(attribute_name)
end

#query_attribute(attribute_name) ⇒ Object



107
108
109
# File 'lib/hash_attributes/hash_attributes.rb', line 107

def query_attribute(attribute_name)
  is_valid_hash_column_attribute_name?(attribute_name) || super(attribute_name)
end

#read_attribute(attribute_name) ⇒ Object



40
41
42
43
44
45
46
47
48
49
# File 'lib/hash_attributes/hash_attributes.rb', line 40

def read_attribute(attribute_name)
  attribute_name = attribute_name.to_s
  if attribute_name == self.class.hash_column
    (super(attribute_name) || {}).with_indifferent_access
  elsif is_valid_hash_column_attribute_name?(attribute_name)
    read_hash_column_attribute(attribute_name)
  else
    super(attribute_name)
  end
end

#read_attribute_before_type_cast(attribute_name) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/hash_attributes/hash_attributes.rb', line 86

def read_attribute_before_type_cast(attribute_name)
  if is_valid_hash_column_attribute_name?(attribute_name)
    read_hash_column_attribute(attribute_name)
  else
    super(attribute_name)
  end
end

#read_hash_column_attribute(attribute_name) ⇒ Object



51
52
53
# File 'lib/hash_attributes/hash_attributes.rb', line 51

def read_hash_column_attribute(attribute_name)
  self.class.deserialize_hash_column_attribute(attribute_name, read_attribute(self.class.hash_column).try(:[], attribute_name))
end

#respond_to?(method_symbol, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
201
202
203
204
205
# File 'lib/hash_attributes/hash_attributes.rb', line 198

def respond_to?(method_symbol, include_private = false)
  if super
    true 
  else
    attribute_name = self.class.extract_attribute_name(method_symbol)
    attribute_name.present? && hash_column_attribute_names.include?(attribute_name)
  end
end

#to_hObject



163
164
165
# File 'lib/hash_attributes/hash_attributes.rb', line 163

def to_h
  serializable_hash.with_indifferent_access
end

#update_columns(attributes) ⇒ Object



189
190
191
192
193
194
195
196
# File 'lib/hash_attributes/hash_attributes.rb', line 189

def update_columns(attributes)
  __hash_column_attributes__ = attributes.except(*self.class.column_names)
  if __hash_column_attributes__.present?
    __hash_column_attributes__ = self.class.serialize_hash_column_attribute(self.class.hash_column, __hash_column_attributes__)
    attributes = attributes.slice(*self.class.column_names).merge({self.class.hash_column => __hash_column_attributes__})
  end
  super(attributes)
end

#write_attribute(attribute_name, value) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/hash_attributes/hash_attributes.rb', line 58

def write_attribute(attribute_name, value)
  attribute_name = attribute_name.to_s
  if attribute_name == self.class.hash_column
    value = value.try(:with_indifferent_access)
    unless read_attribute(self.class.hash_column).try(:with_indifferent_access) == value
      attribute_will_change!(attribute_name)
    end
    super(self.class.hash_column, self.class.serialize_hash_column_attribute(self.class.hash_column, value))
    value
  elsif is_valid_hash_column_attribute_name?(attribute_name)
    write_hash_column_attribute(attribute_name, value)
  else
    super(attribute_name, value)
  end
end

#write_hash_column_attribute(attribute_name, value) ⇒ Object



74
75
76
77
78
79
80
81
# File 'lib/hash_attributes/hash_attributes.rb', line 74

def write_hash_column_attribute(attribute_name, value)
  if value != read_attribute(attribute_name)
    verify_readonly_attribute(attribute_name)
    attribute_will_change!(attribute_name)
    write_attribute(self.class.hash_column, read_attribute(self.class.hash_column).merge(attribute_name => value))
  end
  value
end