Class: ActivePostgrest::Base

Inherits:
Object
  • Object
show all
Includes:
ActiveModelSupport
Defined in:
lib/active_postgrest/base.rb

Constant Summary collapse

POSTGRES_TYPE_CAST =
{
  'date' => :date,
  'timestamp' => :datetime,
  'timestamp with time zone' => :datetime,
  'timestamp without time zone' => :datetime,
  'time' => :time,
  'time with time zone' => :time,
  'time without time zone' => :time,
  'numeric' => :decimal,
  'decimal' => :decimal,
  'real' => :decimal,
  'double precision' => :decimal
}.freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ActiveModelSupport

included, #read_attribute_for_validation, #to_key

Constructor Details

#initialize(attrs = {}, persisted = false, client = nil) ⇒ Base

rubocop:disable Style/OptionalBooleanParameter



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

def initialize(attrs = {}, persisted = false, client = nil) # rubocop:disable Style/OptionalBooleanParameter
  types = self.class.attribute_types
  @attributes = attrs.to_h.transform_keys(&:to_s).to_h do |k, v|
    [k, types[k] ? cast_attribute(v, types[k]) : v]
  end
  @new_record = !persisted
  @destroyed  = false
  @_client    = client
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/active_postgrest/base.rb', line 218

def method_missing(name, *args)
  key = name.to_s
  if key.end_with?('=')
    attr = key.delete_suffix('=')
    if @attributes.key?(attr)
      type = self.class.attribute_types[attr]
      return @attributes[attr] = type ? cast_attribute(args.first, type) : args.first
    end
  elsif @attributes.key?(key)
    return @attributes[key]
  end

  super
end

Class Attribute Details

.schema_nameObject



30
31
32
# File 'lib/active_postgrest/base.rb', line 30

def self.schema_name
  @schema_name || (superclass.schema_name if superclass.respond_to?(:schema_name))
end

.table_nameObject



22
23
24
# File 'lib/active_postgrest/base.rb', line 22

def self.table_name
  @table_name ||= name.demodulize.underscore.pluralize
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



210
211
212
# File 'lib/active_postgrest/base.rb', line 210

def attributes
  @attributes
end

Class Method Details

.allObject



137
# File 'lib/active_postgrest/base.rb', line 137

def self.all                   = relation

.and_where(conditions) ⇒ Object



145
# File 'lib/active_postgrest/base.rb', line 145

def self.and_where(conditions) = relation.and_where(conditions)

.anonymousObject



139
# File 'lib/active_postgrest/base.rb', line 139

def self.anonymous             = relation.anonymous

.any?Boolean

Returns:

  • (Boolean)


169
# File 'lib/active_postgrest/base.rb', line 169

def self.any?         = relation.any?

.attribute(name, type) ⇒ Object



55
56
57
58
# File 'lib/active_postgrest/base.rb', line 55

def self.attribute(name, type)
  @attribute_types ||= {}
  @attribute_types[name.to_s] = type
end

.attribute_typesObject



60
61
62
# File 'lib/active_postgrest/base.rb', line 60

def self.attribute_types
  @attribute_types || {}
end

.attributesObject



68
69
70
# File 'lib/active_postgrest/base.rb', line 68

def self.attributes
  schema['properties']&.transform_values { _1['format'] } || {}
end

.belongs_to(name, class_name: nil, foreign_key: nil) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/active_postgrest/base.rb', line 72

def self.belongs_to(name, class_name: nil, foreign_key: nil)
  assoc   = name.to_s
  klass   = class_name&.to_s || assoc.camelize
  table   = klass.underscore.pluralize
  fk      = foreign_key&.to_s
  aliased = fk || (klass.underscore != assoc)
  key     = aliased ? assoc : table

  define_method(assoc) do
    val = @attributes[key]
    return nil if val.nil? || (val.is_a?(Array) && val.empty?)

    klass.constantize.new(val.is_a?(Array) ? val.first : val, true)
  end

  define_singleton_method(:"with_#{assoc}") do |fields: []|
    if aliased
      joins(table.to_sym, as: assoc.to_sym, foreign_key: fk&.to_sym, select: fields)
    else
      joins(table.to_sym, select: fields)
    end
  end
end

.connectionObject



46
47
48
49
50
51
52
53
# File 'lib/active_postgrest/base.rb', line 46

def self.connection
  @connection ||
    if superclass.respond_to?(:connection)
      superclass.connection
    else
      ActivePostgrest::Client.new
    end
end

.count(mode = :exact) ⇒ Object



168
# File 'lib/active_postgrest/base.rb', line 168

def self.count(mode = :exact) = relation.count(mode)

.create(attrs) ⇒ Object



174
175
176
# File 'lib/active_postgrest/base.rb', line 174

def self.create(attrs)
  relation.insert(attrs)
end

.create!(attrs) ⇒ Object



178
179
180
# File 'lib/active_postgrest/base.rb', line 178

def self.create!(attrs)
  relation.insert(attrs) || raise(RecordNotSaved.new(self, attrs))
end

.delete_allObject



187
# File 'lib/active_postgrest/base.rb', line 187

def self.delete_all           = relation.delete_all

.embedObject



151
# File 'lib/active_postgrest/base.rb', line 151

def self.embed(...)            = relation.embed(...)

.establish_connection(url: ENV.fetch('POSTGREST_URL'), jwt_token: nil) ⇒ Object



42
43
44
# File 'lib/active_postgrest/base.rb', line 42

def self.establish_connection(url: ENV.fetch('POSTGREST_URL'), jwt_token: nil)
  @connection = ActivePostgrest::Client.new(url, jwt_token)
end

.exists?(filters = {}) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/active_postgrest/base.rb', line 162

def self.exists?(filters = {})
  filters.empty? ? relation.any? : relation.where(filters).any?
end

.find(id) ⇒ Object



157
# File 'lib/active_postgrest/base.rb', line 157

def self.find(id)             = relation.where(id: id).first

.find!(id) ⇒ Object



158
# File 'lib/active_postgrest/base.rb', line 158

def self.find!(id)            = find(id) || raise(ActivePostgrest::RecordNotFound.new(self, id))

.find_by(filters) ⇒ Object



159
# File 'lib/active_postgrest/base.rb', line 159

def self.find_by(filters)     = relation.where(filters).first

.find_by!(filters) ⇒ Object



160
# File 'lib/active_postgrest/base.rb', line 160

def self.find_by!(filters)    = find_by(filters) || raise(ActivePostgrest::RecordNotFound.new(self, filters))

.first(n = nil) ⇒ Object



155
# File 'lib/active_postgrest/base.rb', line 155

def self.first(n = nil)       = n ? relation.limit(n).to_a : relation.first

.has_many(name, class_name: nil) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/active_postgrest/base.rb', line 112

def self.has_many(name, class_name: nil)
  assoc = name.to_s
  klass = class_name&.to_s || assoc.singularize.camelize

  define_method(assoc) do
    val = @attributes[assoc]
    return [] if val.nil?

    (val.is_a?(Array) ? val : [val]).map { klass.constantize.new(_1, true) }
  end

  define_singleton_method(:"with_#{assoc}") do |fields: []|
    embed(assoc.to_sym, fields: fields)
  end
end

.has_one(name, class_name: nil) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/active_postgrest/base.rb', line 96

def self.has_one(name, class_name: nil)
  assoc = name.to_s
  klass = class_name&.to_s || assoc.camelize

  define_method(assoc) do
    val = @attributes[assoc]
    return nil if val.nil? || (val.is_a?(Array) && val.empty?)

    klass.constantize.new(val.is_a?(Array) ? val.first : val, true)
  end

  define_singleton_method(:"with_#{assoc}") do |fields: []|
    embed(assoc.to_sym, fields: fields)
  end
end

.insert(attrs) ⇒ Object



182
# File 'lib/active_postgrest/base.rb', line 182

def self.insert(attrs)        = relation.insert(attrs)

.insert_all(records) ⇒ Object



183
# File 'lib/active_postgrest/base.rb', line 183

def self.insert_all(records)  = relation.insert_all(records)

.joinsObject



150
# File 'lib/active_postgrest/base.rb', line 150

def self.joins(...)            = relation.joins(...)

.last(n = nil) ⇒ Object



156
# File 'lib/active_postgrest/base.rb', line 156

def self.last(n = nil)        = n ? relation.order(primary_key, :desc).limit(n).to_a.reverse : relation.last

.limit(n) ⇒ Object



148
# File 'lib/active_postgrest/base.rb', line 148

def self.limit(n)              = relation.limit(n)

.many?Boolean

Returns:

  • (Boolean)


172
# File 'lib/active_postgrest/base.rb', line 172

def self.many?        = relation.many?

.noneObject



138
# File 'lib/active_postgrest/base.rb', line 138

def self.none                  = relation.none

.none?Boolean

Returns:

  • (Boolean)


170
# File 'lib/active_postgrest/base.rb', line 170

def self.none?        = relation.none?

.not_where(filters) ⇒ Object



143
# File 'lib/active_postgrest/base.rb', line 143

def self.not_where(filters)    = relation.not_where(filters)

.offset(n) ⇒ Object



149
# File 'lib/active_postgrest/base.rb', line 149

def self.offset(n)             = relation.offset(n)

.one?Boolean

Returns:

  • (Boolean)


171
# File 'lib/active_postgrest/base.rb', line 171

def self.one?         = relation.one?

.or_where(conditions) ⇒ Object



144
# File 'lib/active_postgrest/base.rb', line 144

def self.or_where(conditions)  = relation.or_where(conditions)

.orderObject



146
# File 'lib/active_postgrest/base.rb', line 146

def self.order(...)            = relation.order(...)

.pick(*cols) ⇒ Object



167
# File 'lib/active_postgrest/base.rb', line 167

def self.pick(*cols)  = relation.pick(*cols)

.pluck(*cols) ⇒ Object



166
# File 'lib/active_postgrest/base.rb', line 166

def self.pluck(*cols) = relation.pluck(*cols)

.primary_keyObject



34
35
36
# File 'lib/active_postgrest/base.rb', line 34

def self.primary_key
  @primary_key ||= 'id'
end

.primary_key=(key) ⇒ Object



38
39
40
# File 'lib/active_postgrest/base.rb', line 38

def self.primary_key=(key)
  @primary_key = key.to_s
end

.relationObject



132
133
134
135
# File 'lib/active_postgrest/base.rb', line 132

def self.relation
  rel = ActivePostgrest::Relation.new(table_name, connection, self)
  schema_name ? rel.with_schema(schema_name) : rel
end

.reorderObject



147
# File 'lib/active_postgrest/base.rb', line 147

def self.reorder(...)          = relation.reorder(...)

.schemaObject



64
65
66
# File 'lib/active_postgrest/base.rb', line 64

def self.schema
  connection.table_schema(table_name)
end

.scope(name, body) ⇒ Object



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

def self.scope(name, body)
  define_singleton_method(name) { |*args, **kwargs| body.call(*args, **kwargs) }
end

.selectObject



152
# File 'lib/active_postgrest/base.rb', line 152

def self.select(...)           = relation.select(...)

.spreadObject



153
# File 'lib/active_postgrest/base.rb', line 153

def self.spread(...)           = relation.spread(...)

.update_all(attrs) ⇒ Object



186
# File 'lib/active_postgrest/base.rb', line 186

def self.update_all(attrs)    = relation.update_all(attrs)

.upsert(attrs) ⇒ Object



184
# File 'lib/active_postgrest/base.rb', line 184

def self.upsert(attrs)        = relation.upsert(attrs)

.upsert_all(records) ⇒ Object



185
# File 'lib/active_postgrest/base.rb', line 185

def self.upsert_all(records)  = relation.upsert_all(records)

.where(filters = nil) ⇒ Object



142
# File 'lib/active_postgrest/base.rb', line 142

def self.where(filters = nil)  = relation.where(filters)

.with_schema(name) ⇒ Object



141
# File 'lib/active_postgrest/base.rb', line 141

def self.with_schema(name)     = relation.with_schema(name)

.with_token(jwt) ⇒ Object



140
# File 'lib/active_postgrest/base.rb', line 140

def self.with_token(jwt)       = relation.with_token(jwt)

Instance Method Details

#[](key) ⇒ Object



203
# File 'lib/active_postgrest/base.rb', line 203

def [](key) = @attributes[key.to_s]

#[]=(key, value) ⇒ Object



205
206
207
208
209
# File 'lib/active_postgrest/base.rb', line 205

def []=(key, value)
  str_key = key.to_s
  type    = self.class.attribute_types[str_key]
  @attributes[str_key] = type ? cast_attribute(value, type) : value
end

#destroyObject

Raises:

  • (ArgumentError)


266
267
268
269
270
271
272
273
274
# File 'lib/active_postgrest/base.rb', line 266

def destroy
  pk     = self.class.primary_key
  pk_val = @attributes[pk]
  raise ArgumentError, 'Cannot destroy a record without a primary key value' if pk_val.nil?

  _base_relation.where(pk => pk_val).delete_all
  @destroyed = true
  self
end

#destroyed?Boolean

Returns:

  • (Boolean)


201
# File 'lib/active_postgrest/base.rb', line 201

def destroyed?  = @destroyed

#inspectObject



214
215
216
# File 'lib/active_postgrest/base.rb', line 214

def inspect
  "#<#{self.class.name} #{@attributes.map { "#{_1}: #{_2.inspect}" }.join(', ')}>"
end

#new_record?Boolean

Returns:

  • (Boolean)


199
# File 'lib/active_postgrest/base.rb', line 199

def new_record? = @new_record

#persisted?Boolean

Returns:

  • (Boolean)


200
# File 'lib/active_postgrest/base.rb', line 200

def persisted?  = !@new_record && !@destroyed

#reloadObject

Raises:



276
277
278
279
280
281
282
283
284
285
# File 'lib/active_postgrest/base.rb', line 276

def reload
  raise RecordNotFound.new(self.class, nil) unless persisted?

  pk    = self.class.primary_key
  fresh = _base_relation.where(pk => @attributes[pk]).first
  raise RecordNotFound.new(self.class, @attributes[pk]) unless fresh

  @attributes = fresh.attributes
  self
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
236
237
# File 'lib/active_postgrest/base.rb', line 233

def respond_to_missing?(name, include_private = false)
  key  = name.to_s
  attr = key.delete_suffix('=')
  @attributes.key?(attr) || super
end

#saveObject

rubocop:disable Naming/PredicateMethod



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/active_postgrest/base.rb', line 239

def save # rubocop:disable Naming/PredicateMethod
  return false if @destroyed

  if new_record?
    saved = self.class.insert(@attributes)
    return false unless saved

    @attributes = saved.attributes
    @new_record = false
  else
    pk     = self.class.primary_key
    pk_val = @attributes[pk]
    raise ArgumentError, 'Cannot save a record without a primary key value' if pk_val.nil?

    saved = _base_relation.where(pk => pk_val).update_all(scalar_attributes.except(pk)).first
    return false unless saved

    @attributes = saved.attributes
  end
  true
end

#to_hObject



212
# File 'lib/active_postgrest/base.rb', line 212

def to_h = @attributes

#update(attrs) ⇒ Object



261
262
263
264
# File 'lib/active_postgrest/base.rb', line 261

def update(attrs)
  attrs.each { |k, v| @attributes[k.to_s] = v }
  save
end