Giter Club home page Giter Club logo

store_attribute's Introduction

Hey / Привет 👋

My name is Vladimir (or Вова), and I'm a principal backend engineer at @evilmartians.

📕 My book "Layered design for Ruby on Rails applications" is avalable now: Amazon | Packt

I'm working on:

Check out some of my blog posts:

...and conference talks:

store_attribute's People

Contributors

alan-marx avatar bzf avatar dalezak avatar dreikanter avatar fcukit avatar glaszig avatar holamendi avatar ioki-klaus avatar markedmondson avatar palkan avatar sumlare avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

store_attribute's Issues

Reading store attribute with the default value populates changes

Example:

class User < ActiveRecord::Base
  store_attribute :meta, :last_updated_at, :date, default: -> { Date.new } 
end

 u = User.first; nil
=> nil
» u.changes
=> {}
» u.meta['foo']
=> nil
» u.changes
=> {"meta"=>
  [{"last_updated_at"=>Thu, 10 Sep 2020},
   {"last_updated_at"=>Thu, 10 Sep 2020}]}

TypeError: no _dump_data is defined for class Proc with default proc value

Hello!
Found problems when caching the object, inside it is calls Marshal.dump(obj)
The problem occurs when the model has a default with proc
Here's my case

class Course < ApplicationRecord
  store_attribute :settings, :foo, :json, default: {}
end

c1 = Course.find(1)
c2 = Course.find(2)
c1.foo[:test] = 2
# Bad
c1.foo #=> {:test=>2}
c2.foo #=> {:test=>2}

class Course < ApplicationRecord
  store_attribute :settings, :foo, :json, default: -> { {} }
end

c1 = Course.find(1)
c2 = Course.find(2)
c1.foo[:test] = 2
# Good
c1.foo #=> {:test=>2}
c2.foo #=> {}

# But!
Marshal.dump(c1) #=> TypeError: no _dump_data is defined for class Proc

# However no problems with ActiveRecord attribute
class Course < ApplicationRecord
  attribute :foo, :json, default: -> { {} }
end

c1 = Course.find(1)
c1.foo #=> {}
Marshal.dump(c1) #=> "\x04\bo:\vCourse\x0F:\x10@attributeso:\x1EActiveModel::AttributeSet\...

Any ideas how to fix this?

Rails 5.1 support

Could you release a new version of store_attribute supporting the latest version of Rails?

Rails 6.1 support ?

Hello, thanks for the gem!

Currently it throws is not a string or a symbol

using

store :analysis_methods, accessors: [val: :boolean, val2: :boolean], coder: JSON

Validators are not working as expected on stores

Ruby Version:
Ruby 3.0.0
Rails Version:
Rails 7.0.4
Store Attribute Version:
1.0.0

What did you do?

use validators on store

What did you expect to happen?

validation should be effected

What actually happened?

validation not happening - atleast the presence validator is not working well

Example

store_accessor  :store1,
                  :store1_key
store_attribute :store1, :store1_key, default: 'value'

validates :store1, presence: true

weird issue with dirty tracking on rails 7

Tell us about your environment

Ruby Version: 2.7.4

Rails Version: 7.0.0.alpha2

Store Attribute Version: 0.9.1

What did you do?

if i have a stored attribute in the database, change another stored attribute and then call #changes, the already present attribute value gets removed. see example below.

this does not happen without the store_attribute gem.

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "activesupport", "~> 7.0.0.alpha2", require: false
  gem "activerecord", "~> 7.0.0.alpha2", require: false
  gem "store_attribute", "0.9.1", require: false
  gem "sqlite3"
  gem "debug"
end

require "active_support"
require "active_record"
require "store_attribute"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :humen, force: true do |t|
    t.text :meta
  end
end

class Human < ActiveRecord::Base
  store :meta, accessors: [ :social_credit, slave: :boolean ], coder: JSON
  after_initialize { self.social_credit ||= 0.0 }
end

class BugTest < Minitest::Test
  def setup
    meta = { slave: true }
    ActiveRecord::Base.connection.execute "DELETE FROM humen;"
    ActiveRecord::Base.connection.execute "INSERT INTO humen (meta) VALUES ('#{meta.to_json}');"
  end

  def test_without_call_to_changes
    human = Human.first
    assert human.slave

    human.social_credit = 1.0
    human.save!

    assert human.slave
  end

  def test_call_to_changes
    human = Human.first
    assert human.slave
    human.changes

    human.save!
    assert human.reload.slave
  end
end

result:

Run options: -v --seed 28294

# Running:

BugTest#test_call_to_changes = D, [2021-10-07T22:00:17.405408 #64486] DEBUG -- :    (0.1ms)  DELETE FROM humen;
D, [2021-10-07T22:00:17.405693 #64486] DEBUG -- :    (0.1ms)  INSERT INTO humen (meta) VALUES ('{"slave":true}');
D, [2021-10-07T22:00:17.406992 #64486] DEBUG -- :   Human Load (0.1ms)  SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ?  [["LIMIT", 1]]
D, [2021-10-07T22:00:17.410243 #64486] DEBUG -- :   TRANSACTION (0.0ms)  begin transaction
D, [2021-10-07T22:00:17.410463 #64486] DEBUG -- :   Human Update (0.1ms)  UPDATE "humen" SET "meta" = ? WHERE "humen"."id" = ?  [["meta", "{\"social_credit\":0.0}"], ["id", 1]]
D, [2021-10-07T22:00:17.410621 #64486] DEBUG -- :   TRANSACTION (0.0ms)  commit transaction
D, [2021-10-07T22:00:17.411141 #64486] DEBUG -- :   Human Load (0.1ms)  SELECT "humen".* FROM "humen" WHERE "humen"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
0.01 s = F
BugTest#test_without_call_to_changes = D, [2021-10-07T22:00:17.414410 #64486] DEBUG -- :    (0.1ms)  DELETE FROM humen;
D, [2021-10-07T22:00:17.414565 #64486] DEBUG -- :    (0.1ms)  INSERT INTO humen (meta) VALUES ('{"slave":true}');
D, [2021-10-07T22:00:17.414959 #64486] DEBUG -- :   Human Load (0.0ms)  SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ?  [["LIMIT", 1]]
D, [2021-10-07T22:00:17.415384 #64486] DEBUG -- :   TRANSACTION (0.0ms)  begin transaction
D, [2021-10-07T22:00:17.415595 #64486] DEBUG -- :   Human Update (0.1ms)  UPDATE "humen" SET "meta" = ? WHERE "humen"."id" = ?  [["meta", "{\"slave\":true,\"social_credit\":1.0}"], ["id", 2]]
D, [2021-10-07T22:00:17.415784 #64486] DEBUG -- :   TRANSACTION (0.0ms)  commit transaction
0.00 s = .

Finished in 0.011849s, 168.7906 runs/s, 337.5812 assertions/s.

  1) Failure:
BugTest#test_call_to_changes [store_attribute_changes.rb:59]:
Expected nil to be truthy.

2 runs, 4 assertions, 1 failures, 0 errors, 0 skips

What did you expect to happen?

a call to #changes should not change model state.

What actually happened?

it removes the slave column value.

BUG: v1.0.1 issues with escaped json

Tell us about your environment

Ruby Version:
3.0.4

Rails Version:
6.1.5.1

PostgreSQL Version:
12

Store Attribute Version:
1.0.1

What did you do?

We have a model with a json type (example below).

class TestObject < ActiveRecord::Base
  store_accessor :params, form_data: :json
end

What did you expect to happen?

When reading or writing to form_data, we would expect the values within to act like hashes.

What actually happened?

We're getting a lot of json values that are escaped multiple times – as many as three times.

This is causing errors in our code because the values returned are strings rather than (the expected) hashes.

Example value in db (params)
{"form_data": ""{\"user_query\":\"\"}""}
Returned
"{"user_query":""}"
Expected
{"user_query" => ""}

I don't know what could be causing the serialization portion but the deserialization does have something strange (to me) happening. On the line below, "hash[key]" returns an transformed value for form_data "{"user_query":""}" – I would expect the raw value ({"form_data": ""{\"user_query\":\"\"}""}).

https://github.com/palkan/store_attribute/blob/v1.0.1/lib/store_attribute/active_record/type/typed_store.rb#L49

_lookup_cast_type removed in Rails 7 alpha

Tell us about your environment

Ruby Version: 2.7.2

Rails Version: 7.0.0.alpha (from GitHub)

PostgreSQL Version: 13.3

Store Attribute Version: (Latest from GitHub)

What did you do?

We've started looking at using our application at work using Rails 7 from GitHub, while using store_attribute in our models. In doing so, it raised some errors from underlying changes to ActiveRecord.

For full transparency, this is how a few of our store_attribute calls look:

  store_attribute :greeting, :title, :string, prefix: :greeting
  store_attribute :greeting, :description, :string, prefix: :greeting
  store_attribute :greeting, :video, :json, prefix: :greeting

What did you expect to happen?

The error to not be raised :) (though to be fair, it makes sense why the error happens)

What actually happened?

An error was raised (both in the browser and the Rails console)

NoMethodError: undefined method `_lookup_cast_type' for #<Class:0x00007fd1b53d5430>

I tracked it down by removing the store_attributes in our model, and things behaved like expected, when I dug deeper into the gem source I believe this is the only occurrence of using the removed method from Rails 7.

Type::TypedStore.create_from_type(_lookup_cast_type(attr_name, was_type, {}), name, type, **options)

Here is the PR with the removal of this method from Rails: https://github.com/rails/rails/pull/39929/files#diff-2a6a5a31a6210a1e7036784fb31c738e1d2874bb5ae123d03737db02cb198876L290-L300

I wasn't quite sure how would be best to fix the gem for backwards compatibility, so thanks in advance!

BUG: v1.0.1 non-typed attribute names don't include prefix or suffix

Tell us about your environment

Ruby Version:
3.0.4

Rails Version:
6.1.5.1

PostgreSQL Version:
12

Store Attribute Version:
1.0.1

What did you do?

We've attempted to use prefixes with non-typed attributes (example below).

class Space::TestObject < ActiveRecord::Base
  store_accessor :notes, :for_manager, prefix: :note
end

What did you expect to happen?

A method for "note_for_manager" to be defined.

What actually happened?

ActiveModel::UnknownAttributeError: unknown attribute 'note_for_manager' for Space::TestObect.

It looks like the prefix (and suffix) values were unintentionally omitted in a recent changeset. https://github.com/palkan/store_attribute/blob/v1.0.1/lib/store_attribute/active_record/store.rb#L63

As a workaround, providing a type to the attribute works as expected.

class Space::TestObject < ApplicationRecord
  store_accessor :notes, for_manager: jsonb, prefix: :note
end

Default value questions v0.8x -> v1.x

Not a specific issue, just looking to clarify why default values work like they do. Following along with #6 and #7 it looks like it should follow the Attribute API but things seem to have changed in v1.x...

Given:

class User < ActiveRecord::Base
  store_attribute :preferences, :color, :string, default: 'red'
end
INSERT INTO "users" ("id", "preferences") VALUES (1, '{}');

v0.8.1:

User.new.color => "red"
User.new.preferences => {"color"=>"red"}
User.first.color => "red"
User.first.preferences => {"color"=>"red"}

v1.0.2

User.new.color => "red"
User.new.preferences => {"color"=>"red"}
User.first.color => nil
User.first.preferences => nil

An example of this in practice is if we add a new store_attribute to an existing persisted object, I'd expect the default to be set on load. Obviously this would cause the object to be marked dirty the first time it's initialized with the new store_attribute (which gets us into all the trouble we had in #23) but at least persisting would update the serialized value so that would only happen once.

Anyway, just looking for where the decision was made and why, it's making upgrading riskier!

Dirty tracking methods erroring out when used in callback proc

Tell us about your environment

Ruby Version: 3.0.0

Rails Version: 6.1.4.1

PostgreSQL Version: 13.2

Store Attribute Version: 0.9.0

What did you do?

I ran bundle update, which updated store_attribute to 0.9.0 from 0.8.1.

What did you expect to happen?

What actually happened?

After update, my test suite is failing on a model that uses store_attribute and uses dirty tracking methods in conditional callbacks;

  store_attribute :settings, :apis, :string  
  before_update :activate, if: -> { apis_changed? }

On binding and calling the method directly I get;

NoMethodError: undefined method `each' for nil:NilClass
from /home/deploy/.bundle/gems/store_attribute-0.9.0/lib/store_attribute/active_record/store.rb:74:in `block (3 levels) in store_accessor'

BUG: v1.0.1 changes the behavior of active model

Tell us about your environment

Ruby Version:
3.0.4

Rails Version:
6.1.5.1

PostgreSQL Version:
12

Store Attribute Version:
1.0.1

What did you do?

Our rails app includes classes that aren't ActiveRecord classes. An example is below.

class TestObject
  include ActiveModel::Model
  include ActiveModel::Validations
  include ActiveModel::Dirty

  define_attribute_methods :content

  attr_reader :content

  def content=(value)
    @content = value
    content_will_change!
  end

  def reset_dirty_tracking
    changes_applied
  end
end

What did you expect to happen?

The behavior of class should not be changed by store_attribute.

What actually happened?

The following error occurs.

Minitest::UnexpectedError:         NoMethodError: undefined method `[]' for #            lib/rails_ext/active_record_mutation_tracker_extention.rb:8:in `change_to_attribute'

It appears "StoreAttribute::MutationTracker#change_to_attribute" expects the "attributes" to be an instance of "ActiveModel::LazyAttributeSet" when it (in this case) is an instance of the class – TestObject.

Storing Array Type Objects

Is it possible to store an :array type?

The documentation says type A symbol such as :string or :integer, or a type object to be used for the accessor, but wondering if I can store arrays too. For example, something like this:

  store_attribute :details, :chapter_ids_stored, :array, default: []
  store_attribute :details, :chapter_ids_stored, :object, default: []

I'm currently storing relationship ids in Redis cache, since they are frequently used and costly to calculate. But wondering whether I could use store_attribute instead so store these ids in the actual record, then replace them upon updating the model.

I could store it as a :string converting to a comma separated list first, and then back again to an array, but would be nicer if it was possible to just store as an array. Is there a way to achieve this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.