Coder Social home page Coder Social logo

Comments (5)

DmitryTsepelev avatar DmitryTsepelev commented on May 24, 2024

Hi @23tux, thanks for your interest! I understand the use case you have, but there are some issues with the implementation I'd like to discuss to find out the best way to solve it:

  1. What should happen when scope is defined inside the class that includes StoreModel::Model, but then this class is used with .to_type? We probably could define scopes inside the block like
class Product < ApplicationRecord
  attribute :properties, Property.to_array_type do
    scope :hidden, ->{ select { |prop| prop.key.start_with?("_") } }
    scope :english, locale: "en"
  end
end
  1. (props1 + props2).uniq.hidden.english looks tricky, because in this case in this case it looks like we'd have to implement + (should we have other math operators? 🙂)

from store_model.

23tux avatar 23tux commented on May 24, 2024

Hi @DmitryTsepelev, thanks for the reply!

  1. I think defining the scopes inside the block would be just fine, looks good to me!
  2. Yes, you're right, this looks like we would have to implement all the array methods like +, &, uniq, select... Don't know if this is a good solution? Maybe we can find some inspiration in ActiveRecord and how they handle such scope use cases.

Another approach I just tried, defining scopes like this:

class Product < ApplicationRecord
  attribute :properties, Property.to_array_type
  def properties(scope = :hidden)
    Property.public_send(scope, super())
  end
end

class Property
  def self.hidden(props)
    props.select(&:hidden?)
  end
end

So when you pass a scope to #properties it get's filtered.

But this has a major drawback: Doing a product.properties.push(...) does nothing. It never gets written into the json column, because it's a new array every time.

So I was thinking, what should happen when you add a property to a scoped properties list? Should this throw an error? Or just ignore the changes and only let push to the unscoped array? Maybe we could define a general #add method on the collection? What do you think?

from store_model.

DmitryTsepelev avatar DmitryTsepelev commented on May 24, 2024

Yep, adding #add with the optional scope param sounds logical. The bad thing is that this approach is still hard to generalize and include to the DSL 😅

from store_model.

23tux avatar 23tux commented on May 24, 2024

I think I have a solution that works for me now:

class Product < ApplicationRecord
  attribute :properties, Property.to_array_type, default: []
  def properties(scope = :visible)
    Property.scope(super(), scope)
  end
end

class Property
  include StoreModel::Model

  attribute :key, :string
  attribute :value, :string

  def visible?
    !hidden?
  end

  def hidden?
    key.to_s.start_with?("_")
  end

  def self.visible(props)
    props.select(&:visible?)
  end

  def self.hidden(props)
    props.select(&:hidden?)
  end

  def self.all(props)
    props.clone
  end

  def self.scope(properties, scope)
    public_send(scope ||= :all, properties).tap do |scoped|
      scoped.define_singleton_method(:add) do |prop|
        properties.push(prop)
      end
      scoped.define_singleton_method(:remove) do |key|
        properties.delete_if { |prop| prop.key == key }
      end
      scoped.define_singleton_method(:scope) { scope }
    end.freeze
  end
end

I know this is specific to my use case, but maybe a similar concept could be implemented into the library. The key takeaways for me where:

  • overwriting #properties with a scope parameter and calling super() in it to access the underlying json column
  • making the scoped array frozen so that no one would think a push to a scoped array would change the underlying json column
  • defining #add and #remove methods for manipulating the underlying array
  • defining a #scope method for debugging purpose, so that you know which scope is applied

This is not chainable so far, but enough for my specific use case (for now).

from store_model.

DmitryTsepelev avatar DmitryTsepelev commented on May 24, 2024

Good work on that! Looks like this approach could be generalized – I'll be happy to merge the PR in. If you have some time to play with it a bit more, there are a couple more API design questions to solve:

  1. We need to find a way to define scopes with DSL like you proposed initially
  2. We need to generate methods like Product#properties dynamically

I think it would be great to isolate the whole thing in the separate module (StoreModel::Model::Scoped ?). Also, we need to have a method in StoreModel::Model for generating Product#properties (something like store_model_scope :hidden, :properties, ->(props) { ... }) and patching Properties class with scope method.

from store_model.

Related Issues (20)

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.