Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema for Expressions #718

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
523859b
Replace specs for expressions with shared library
bkeepers Mar 30, 2023
10fa1b4
Move expressions schemas into this repo
bkeepers Mar 30, 2023
a72ae35
Make Ruby 2.6 happy
bkeepers Mar 30, 2023
87c3de4
Use vitest to run tests
bkeepers Mar 31, 2023
0d065b5
Shuffle around somethings in schema.json and rename defs
bkeepers Mar 31, 2023
ee46fab
Add Min/Max functions
bkeepers Mar 31, 2023
6a2e622
Rename url-unfriendly defs
bkeepers Mar 31, 2023
08b03c1
Move expression examples out of test dir
bkeepers Mar 31, 2023
5004b6f
Initial docs for implementing functions
bkeepers Mar 31, 2023
4c35f3e
Run JS lint/tests
bkeepers Mar 31, 2023
c51eda3
Add not about running tests to expression docs
bkeepers Mar 31, 2023
50dc917
Fix README formatting
bkeepers Mar 31, 2023
6262795
Remove use of path
bkeepers Apr 1, 2023
4a10eaf
Simplfy schemas by making implementation cast function arguments to a…
bkeepers Apr 1, 2023
48308dd
Add json_schemer as a dependency
bkeepers Apr 1, 2023
81a1d65
Fix JS lint
bkeepers Apr 1, 2023
b8279c5
Validate schemas in strict mode
bkeepers Apr 2, 2023
270f290
$defs => definitions for draft 7
bkeepers Apr 3, 2023
8f69da4
Build sourcemaps
bkeepers Apr 10, 2023
a498b3a
Add explorer to inspect information about the schema
bkeepers Apr 4, 2023
a2c501a
Add simple js expression model
bkeepers Apr 4, 2023
a3b2076
Add validate method to Expression/Constant
bkeepers Apr 5, 2023
ad07179
Add simple schema class to browse and validate schemas
bkeepers Apr 8, 2023
d96ec78
Add proxying back to schema to resolve refs
bkeepers Apr 9, 2023
d134ae6
Add/Subtract/Multiply/Divide functions
bkeepers Apr 10, 2023
b4e7035
Fix bug where ajv eagerly resolves ref in array items
bkeepers Apr 10, 2023
7c5016b
use exported Schema class where possible
bkeepers Apr 10, 2023
26a08f3
Pair schema with expression and constant
bkeepers Apr 12, 2023
82cf6ac
Add operator keyword to schemas
bkeepers Apr 12, 2023
3f6d241
Allow null as a constant
bkeepers Jul 17, 2023
cfecaf5
Specify json_schemer version number
bkeepers Jul 17, 2023
e3b32c4
JS: Define expression.add(…) to build new expression
bkeepers Jul 19, 2023
0848411
Add parent to expressions, refactor
bkeepers Jul 25, 2023
639d2a7
Use external package for schemas
bkeepers Oct 12, 2023
8aa356c
Merge branch 'main' into expressions-schema
bkeepers Oct 12, 2023
6512d63
Add flipper-expressions-schema as separate gem for now
bkeepers Oct 12, 2023
21a0806
Merge remote-tracking branch 'origin/main' into expressions-schema
bkeepers Oct 12, 2023
9725f56
Ensure schemas are downloaded
bkeepers Oct 12, 2023
8e1bfb1
Ensure node is setup before bundling
bkeepers Oct 12, 2023
7fc02ff
Revert renaming of ci job
bkeepers Oct 12, 2023
15099b9
English properly
bkeepers Oct 12, 2023
6640afc
Don't auth to install npm/gems from github
bkeepers Oct 12, 2023
ed90c55
Try using the gem post_install hook to avoid issue with GitHub Actions
bkeepers Oct 13, 2023
6350f0e
Remove actions/cache, just rely on ruby/node actions for caching
bkeepers Oct 13, 2023
b36dd09
Run npm install before setting up ruby
bkeepers Oct 13, 2023
5470e59
Rename expressions repo
bkeepers Oct 13, 2023
afb896a
Merge remote-tracking branch 'origin/main' into expressions-schema
bkeepers Nov 10, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,16 @@ jobs:
mongodb-version: 4.0
- name: Check out repository code
uses: actions/checkout@v4
- name: Do some action caching
uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
# Install gems/npm packages from GitHub fails without this
persist-credentials: false
- name: Install libpq-dev
run: sudo apt-get -yqq install libpq-dev
- name: Set up Node
uses: actions/setup-node@v3
with:
cache: 'npm'
- run: npm clean-install
- name: Set up Ruby ${{ matrix.ruby }}
uses: ruby/setup-ruby@v1
with:
Expand Down
12 changes: 5 additions & 7 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,13 @@ jobs:
mongodb-version: 4.0
- name: Check out repository code
uses: actions/checkout@v4
- name: Do some action caching
uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
- name: Install libpq-dev
run: sudo apt-get -yqq install libpq-dev
- name: Set up Node
uses: actions/setup-node@v3
with:
cache: 'npm'
- run: npm clean-install
- name: Set up Ruby ${{ matrix.ruby }}
uses: ruby/setup-ruby@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ log
.sass-cache
bin
.DS_Store
node_modules
.tool-versions
3 changes: 3 additions & 0 deletions Guardfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ guard 'rspec', rspec_options do
watch(/shared_adapter_specs\.rb$/) { 'spec' }
watch('spec/helper.rb') { 'spec' }

watch(%r{lib/flipper/expressions}) { 'spec/flipper/expressions_schema_spec.rb' }
watch(%r{node_modules/@flippercloud.io/expressions}) { 'spec/flipper/expressions_schema_spec.rb' }

# To run all specs on every change... (useful with focus and fit)
# watch(%r{.*}) { 'spec' }
end
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require 'flipper/version'
# gem uninstall flipper flipper-ui flipper-redis
desc 'Build gem into the pkg directory'
task :build do
sh 'npm clean-install --silent'
FileUtils.rm_rf('pkg')
Dir['*.gemspec'].each do |gemspec|
system "gem build #{gemspec}"
Expand Down
37 changes: 37 additions & 0 deletions flipper-expressions-schema.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/flipper/version', __FILE__)
require File.expand_path('../lib/flipper/metadata', __FILE__)

Gem::Specification.new do |gem|
SCHEMAS_DIR = File.expand_path("node_modules/@flippercloud.io/expressions/schemas", __dir__)

# Ensure schemas are downloaded when installed as a git dependency.
# This will be handled by rake when the gem is built and published.
Gem.post_install do
unless File.exist?(SCHEMAS_DIR)
warn "Getting schemas from @flippercloud.io/expressions..."
Dir.chdir(__dir__) { exec "npm install --silent" }
end
end

gem.authors = ['John Nunemaker']
gem.email = '[email protected]'
gem.summary = 'ActiveRecord adapter for Flipper'
gem.license = 'MIT'
gem.homepage = 'https://www.flippercloud.io/docs/expressions'

gem.files = [
'package.json',
'package-lock.json',
'lib/flipper-expressions-schema.rb',
'lib/flipper/expression/schema.rb',
'lib/flipper/version.rb',
] + Dir['node_modules/@flippercloud.io/expressions/schemas/*.json']
gem.name = 'flipper-expressions-schema'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be flipper-expression-schemas? The ruby class is Flipper::Expression::Schema to avoid conflicts with functions declared in Flipper::Expressions::*, but maybe those should be renamed to Flipper::Function::*. The JS package is @flippercloud.io/expressions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you just wondering if it should be plural? I think matching to ruby is fine even if singular.

gem.require_paths = ['lib']
gem.version = Flipper::VERSION
gem.metadata = Flipper::METADATA

gem.add_dependency 'flipper', "~> #{Flipper::VERSION}"
gem.add_dependency 'json_schemer', '~> 1.0'
end
2 changes: 1 addition & 1 deletion flipper.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugin_files = []
plugin_test_files = []

Dir['flipper-*.gemspec'].map do |gemspec|
spec = eval(File.read(gemspec))
spec = Bundler.load_gemspec(gemspec)
plugin_files << spec.files
plugin_test_files << spec.files
end
Expand Down
1 change: 1 addition & 0 deletions lib/flipper-expressions-schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "flipper/expression/schema"
18 changes: 13 additions & 5 deletions lib/flipper/expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ def self.build(object)

case object
when Hash
name = object.keys.first
args = object.values.first
unless name
if(object.keys.size != 1)
raise ArgumentError, "#{object.inspect} cannot be converted into an expression"
end

new(name, Array(args).map { |o| build(o) })
when String, Numeric, FalseClass, TrueClass
name = object.keys.first
args = object.values.first

# Ensure args are an array, but we can't just use Array(args) because it will convert a Hash to Array
args = args.is_a?(Hash) ? [args] : Array(args)

new(name, args.map { |o| build(o) })
when String, Numeric, FalseClass, TrueClass, nil
Expression::Constant.new(object)
when Symbol
Expression::Constant.new(object.to_s)
Expand Down Expand Up @@ -56,6 +60,10 @@ def value
}
end

def validate
Schema.new.validate(value)
end

private

def call_with_context?
Expand Down
4 changes: 4 additions & 0 deletions lib/flipper/expression/constant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def eql?(other)
other.is_a?(self.class) && other.value == value
end
alias_method :==, :eql?

def validate
Schema.new.validate(value)
end
end
end
end
33 changes: 33 additions & 0 deletions lib/flipper/expression/schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "json_schemer"

module Flipper
class Expression
class Schema < JSONSchemer::Schema::Draft7
PATH = Pathname.new(File.expand_path("../../../node_modules/@flippercloud.io/expressions", __dir__))

def self.schemas
@schemas ||=
Hash[
PATH
.glob("schemas/*.json")
.map { |path| [File.basename(path), JSON.parse(File.read(path))] }
]
end

def self.examples
PATH
.glob("examples/*.json")
.map { |path| [File.basename(path), JSON.parse(File.read(path))] }
end

def initialize(schema = self.class.schemas["schema.json"])
super(
schema,
insert_property_defaults: true,
ref_resolver:
lambda { |url| self.class.schemas[File.basename(url.path)] }
)
end
end
end
end
9 changes: 9 additions & 0 deletions lib/flipper/expressions/add.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Flipper
module Expressions
class Add
def self.call(left, right)
left + right
end
end
end
end
9 changes: 9 additions & 0 deletions lib/flipper/expressions/divide.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Flipper
module Expressions
class Divide
def self.call(left, right)
left / right
end
end
end
end
2 changes: 1 addition & 1 deletion lib/flipper/expressions/duration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Duration
"year" => 31_556_952 # length of a gregorian year (365.2425 days)
}.freeze

def self.call(scalar, unit = 'second')
def self.call(scalar, unit)
unit = unit.to_s.downcase.chomp("s")

unless scalar.is_a?(Numeric)
Expand Down
11 changes: 11 additions & 0 deletions lib/flipper/expressions/max.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "flipper/expression"

module Flipper
module Expressions
class Max
def self.call(*args)
args.max
end
end
end
end
11 changes: 11 additions & 0 deletions lib/flipper/expressions/min.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require "flipper/expression"

module Flipper
module Expressions
class Min
def self.call(*args)
args.min
end
end
end
end
9 changes: 9 additions & 0 deletions lib/flipper/expressions/multiply.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Flipper
module Expressions
class Multiply
def self.call(left, right)
left * right
end
end
end
end
2 changes: 1 addition & 1 deletion lib/flipper/expressions/percentage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Flipper
module Expressions
class Percentage
def self.call(value)
value.to_f.clamp(0, 100)
value.clamp(0, 100)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/flipper/expressions/percentage_of_actors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class PercentageOfActors

def self.call(text, percentage, context: {})
prefix = context[:feature_name] || ""
Zlib.crc32("#{prefix}#{text}") % (100 * SCALING_FACTOR) < percentage * SCALING_FACTOR
Zlib.crc32("#{prefix}#{text}") % (100 * SCALING_FACTOR) < percentage.clamp(0, 100) * SCALING_FACTOR
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions lib/flipper/expressions/subtract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Flipper
module Expressions
class Subtract
def self.call(left, right)
left - right
end
end
end
end
2 changes: 1 addition & 1 deletion lib/flipper/expressions/time.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Flipper
module Expressions
class Time
def self.call(value)
::Time.parse(value)
::Time.iso8601(value)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/flipper/gates/expression.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def open?(context)
end

def protects?(thing)
thing.is_a?(Flipper::Expression) || thing.is_a?(Hash)
thing.is_a?(Flipper::Expression) || thing.is_a?(Flipper::Expression::Constant) || thing.is_a?(Hash)
end

def wrap(thing)
Expand Down
Loading