msfl 0.0.1.pre.rc1 → 0.0.1.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/msfl.rb +1 -0
- data/lib/msfl/datasets/base.rb +37 -0
- data/lib/msfl/datasets/cars.rb +15 -0
- data/lib/msfl/datasets/movies.rb +2 -0
- data/lib/msfl/sinatra.rb +90 -0
- data/msfl.gemspec +1 -1
- data/spec/msfl/shared_examples.rb +23 -0
- data/spec/msfl/sinatra/helpers_spec.rb +98 -0
- data/spec/msfl/sinatra_spec.rb +97 -0
- data/spec/spec_helper.rb +3 -1
- metadata +7 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: daafbe97f5c20cfcd8c01f85f037d2ce541f79ea
|
4
|
+
data.tar.gz: 5dc889069b814f410425cd899a195affb0d5033e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82a657a00b0dfa31b645d4a23bf587768510dd03dbeaa659543e11570af810688e29dad84475c4632ef1dc0c7f9f1b2c3115af9f6579f764b9d99be54268c8e5
|
7
|
+
data.tar.gz: be3361401722d576c5a9cb66dd1f4c4883f1d9abdf9328fe52f7dce1966deb09ffe7b16cebe4d21496847b23cf9642465c3f018196da031439c4726c1c63258d
|
data/lib/msfl.rb
CHANGED
data/lib/msfl/datasets/base.rb
CHANGED
@@ -4,6 +4,43 @@ module MSFL
|
|
4
4
|
|
5
5
|
include Validators::Definitions::HashKey
|
6
6
|
|
7
|
+
# This class singleton code and the register_dataset method are a cute nicety for conveniently registering
|
8
|
+
# new datasets from within the datasets themselves
|
9
|
+
class << self
|
10
|
+
def registered_datasets
|
11
|
+
@registered_datasets ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def registered_datasets=(registered_datasets)
|
15
|
+
@registered_datasets = registered_datasets
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Register a MSFL::Dataset as a registered dataset so that other code can reference the dataset using its
|
20
|
+
# name as a symbol, instead of having to pass around the class name.
|
21
|
+
#
|
22
|
+
# If no arguments are provided it registers the current class and sets its name to the class name downcased
|
23
|
+
# The dataset being registered can be overridden. The dataset name (how one refers to the dataset as a symbol)
|
24
|
+
# can also be overridden.
|
25
|
+
#
|
26
|
+
# @todo add tests
|
27
|
+
#
|
28
|
+
# @meta-spinach
|
29
|
+
# @param dataset [Class] optionally specify a dataset to register (use this when registration occurs outside
|
30
|
+
# of a dataset's class scope)
|
31
|
+
# @param opts [Hash] options
|
32
|
+
# notable option: :name (it allows you to override the dataset name)
|
33
|
+
def self.register_dataset(dataset = nil, opts = {})
|
34
|
+
dataset ||= self
|
35
|
+
dataset_name = opts[:name] if opts.has_key?(:name)
|
36
|
+
dataset_name ||= dataset.name
|
37
|
+
dataset_name.slice! "MSFL::Datasets::"
|
38
|
+
dataset_name.downcase!
|
39
|
+
registered_datasets = MSFL::Datasets::Base.registered_datasets
|
40
|
+
registered_datasets[dataset_name.to_sym] = dataset
|
41
|
+
MSFL::Datasets::Base.registered_datasets = registered_datasets
|
42
|
+
end
|
43
|
+
|
7
44
|
# The descendant class MUST override this method otherwise all field validations will fail
|
8
45
|
#
|
9
46
|
# The method defines an array of symbols, indicating what fields are supported for the Dataset
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module MSFL
|
4
|
+
module Datasets
|
5
|
+
# This is a fake dataset definition that shows the structure for composing your own and is used for testing
|
6
|
+
# msfl
|
7
|
+
class Cars < ::MSFL::Datasets::Base
|
8
|
+
register_dataset
|
9
|
+
|
10
|
+
def fields
|
11
|
+
[:make, :model, :year, :value]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/msfl/datasets/movies.rb
CHANGED
data/lib/msfl/sinatra.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module MSFL
|
2
|
+
module Sinatra
|
3
|
+
class << self
|
4
|
+
|
5
|
+
attr_accessor :valid_filter
|
6
|
+
|
7
|
+
def valid_filter
|
8
|
+
@valid_filter ||= nil
|
9
|
+
end
|
10
|
+
|
11
|
+
# Extracts the filter clause from a Sinatra request params hash and parsers the filter
|
12
|
+
#
|
13
|
+
# @param params [Hash] the Sinatra request params
|
14
|
+
# @return [Object] the Ruby-ified MSFL filter
|
15
|
+
def parse_filter_from(params)
|
16
|
+
filter = params[:filter]
|
17
|
+
MSFL::Parsers::JSON.parse filter.to_json unless filter.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Extracts the dataset name from the Sinatra params. It then returns a new instance of the specified
|
21
|
+
# dataset.
|
22
|
+
#
|
23
|
+
# @param params [Hash] the Sinatra request params
|
24
|
+
# @return [MSFL::Datasets::Base, Nil] a new instance of the specified dataset, if it can be found, otherwise nil
|
25
|
+
def dataset_from(params)
|
26
|
+
dataset_name = params[:dataset].to_sym unless params[:dataset].nil?
|
27
|
+
dataset_name ||= nil
|
28
|
+
Datasets::Base.registered_datasets[dataset_name].new if Datasets::Base.registered_datasets.has_key?(dataset_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a semantic validator instance that is ready to validate the dataset
|
32
|
+
#
|
33
|
+
# @param params [Hash] the Sinatra request params
|
34
|
+
# @return [MSFL::Validators::Semantic] a validator instance ready to validate filters for the dataset
|
35
|
+
def validator_from(params)
|
36
|
+
MSFL::Validators::Semantic.new dataset_from(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate the MSFL filter in the Sinatra request's params hash
|
40
|
+
#
|
41
|
+
# @param params [Hash] the Sinatra request params
|
42
|
+
# @return [Bool]
|
43
|
+
def validate(params)
|
44
|
+
validator = validator_from params
|
45
|
+
parsed_filter = parse_filter_from params
|
46
|
+
result = validator.validate parsed_filter
|
47
|
+
@valid_filter = parsed_filter if result
|
48
|
+
result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module Helpers
|
53
|
+
# This method extracts the dataset name and filter from the Sinatra params. It also performs semantic validation
|
54
|
+
# of the filter relative to the dataset.
|
55
|
+
#
|
56
|
+
# The method also has the side effect, when the filter is valid or setting the valid_filter singleton class
|
57
|
+
# instance variable valid_filter to the Ruby-ified parsed filter.
|
58
|
+
#
|
59
|
+
# @param params [Hash] this should be the params variable from the request context
|
60
|
+
# @return [Bool] returns true if the filter is valid, false otherwise.
|
61
|
+
def msfl_valid?(params)
|
62
|
+
Sinatra.validate params
|
63
|
+
end
|
64
|
+
|
65
|
+
# This method returns the valid MSFL filter. If the valid filter has already been extracted from the parameters
|
66
|
+
# it is returned, otherwise if the optional params argument is specified it will be sent to MSFL::Sinatra.validate
|
67
|
+
# and if the validation is successful the valid filter is returned. If the valid filter still cannot be determined
|
68
|
+
# then the method raises an ArgumentError.
|
69
|
+
#
|
70
|
+
# @param params [Hash] optionally pass in the Sinatra request parameters - this is intended to be used when no
|
71
|
+
# previously successful call to msfl_valid? has been made, so that when you just need the filter you can skip
|
72
|
+
# writing msfl_valid?(params) in your code.
|
73
|
+
# @return [Object] the Ruby-ified and validated MSFL filter
|
74
|
+
def msfl_filter(params = nil)
|
75
|
+
filter = Sinatra.valid_filter
|
76
|
+
if filter.nil?
|
77
|
+
Sinatra.validate params
|
78
|
+
filter = Sinatra.valid_filter
|
79
|
+
end
|
80
|
+
raise ArgumentError, "A valid filter could not be located in msfl_filter." if filter.nil?
|
81
|
+
filter
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Sinatra specific registration hook
|
86
|
+
def self.registered(app)
|
87
|
+
app.helpers Helpers if app.respond_to?(:helpers)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/msfl.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'msfl'
|
3
|
-
s.version = '0.0.1-
|
3
|
+
s.version = '0.0.1-rc2'
|
4
4
|
s.date = '2015-03-05'
|
5
5
|
s.summary = "MSFL in Ruby"
|
6
6
|
s.description = "Serializers, validators, and other tasty goodness for the Mattermark Semantic Filter Language in Ruby."
|
@@ -0,0 +1,23 @@
|
|
1
|
+
shared_examples_for "an invocation of MSFL::Sinatra.validate" do
|
2
|
+
|
3
|
+
let(:params) { { dataset: dataset, filter: filter } }
|
4
|
+
|
5
|
+
context "when params[:dataset] is :movies" do
|
6
|
+
|
7
|
+
let(:dataset) { :movies }
|
8
|
+
|
9
|
+
context "when params[:filter] is a valid filter" do
|
10
|
+
|
11
|
+
let(:filter) { { title: "Gone with the wind" } }
|
12
|
+
|
13
|
+
it { is_expected.to be true }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when params[:filter] is an invalid filter" do
|
17
|
+
|
18
|
+
let(:filter) { { notavalidfield: "some arbitrary value" } }
|
19
|
+
|
20
|
+
it { is_expected.to be false }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "MSFL::Sinatra::Helpers" do
|
4
|
+
|
5
|
+
let(:sinatra_app_class) do
|
6
|
+
klass = class SinatraTestAppClass
|
7
|
+
# In a proper Sinatra app there would be no need to this because when the extension is registered the helpers
|
8
|
+
# are automatically mixed in
|
9
|
+
include MSFL::Sinatra::Helpers
|
10
|
+
end
|
11
|
+
klass
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:sinatra_app) { sinatra_app_class.new }
|
15
|
+
|
16
|
+
let(:params) { nil }
|
17
|
+
|
18
|
+
describe "#msfl_valid?" do
|
19
|
+
|
20
|
+
subject(:mut) { sinatra_app.msfl_valid? params }
|
21
|
+
|
22
|
+
it_behaves_like "an invocation of MSFL::Sinatra.validate"
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#msfl_filter" do
|
26
|
+
|
27
|
+
subject(:mut) { sinatra_app.msfl_filter params }
|
28
|
+
|
29
|
+
context "when Sinatra.valid_filter is a valid MSFL filter" do
|
30
|
+
|
31
|
+
let(:valid_filter) { { arbitrary: "hash" } }
|
32
|
+
|
33
|
+
before { MSFL::Sinatra.valid_filter = valid_filter }
|
34
|
+
|
35
|
+
it "is the valid MSFL filter" do
|
36
|
+
expect(mut).to eq valid_filter
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when Sinatra.valid_filter is nil" do
|
42
|
+
|
43
|
+
before(:each) { MSFL::Sinatra.valid_filter = nil }
|
44
|
+
|
45
|
+
let(:params) { { filter: filter, dataset: dataset } }
|
46
|
+
|
47
|
+
let(:filter) { nil }
|
48
|
+
|
49
|
+
let(:dataset) { nil }
|
50
|
+
|
51
|
+
context "when params[:dataset] is nil" do
|
52
|
+
|
53
|
+
context "when params[:filter] is valid json" do
|
54
|
+
|
55
|
+
let(:filter) { { notavalidmsflkeybutokjson: ["a", "b"] } }
|
56
|
+
|
57
|
+
it "raises a NoMethodError" do
|
58
|
+
expect { mut }.to raise_error NoMethodError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when params[:filter] is not valid json" do
|
64
|
+
|
65
|
+
let(:filter) { "iamnotvalidjson" }
|
66
|
+
|
67
|
+
it "raises a JSON::ParserError" do
|
68
|
+
expect { mut }.to raise_error JSON::ParserError
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when params[:filter] is nil" do
|
73
|
+
|
74
|
+
it "raises an ArgumentError" do
|
75
|
+
expect { mut }.to raise_error ArgumentError
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when params[:dataset] is a valid Dataset" do
|
80
|
+
|
81
|
+
let(:dataset) { :cars }
|
82
|
+
|
83
|
+
context "when params[:filter] is a valid MSFL filter" do
|
84
|
+
|
85
|
+
let(:filter) { { make: { in: inner_array } } }
|
86
|
+
|
87
|
+
let(:inner_array) { ["Chevy", "Tesla"] }
|
88
|
+
|
89
|
+
let(:msfl_filter) { { make: { in: MSFL::Types::Set.new(inner_array) } } }
|
90
|
+
|
91
|
+
it "is the valid MSFL filter" do
|
92
|
+
expect(mut).to eq msfl_filter
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe "MSFL::Sinatra" do
|
5
|
+
|
6
|
+
describe ".registered" do
|
7
|
+
|
8
|
+
let(:app) do
|
9
|
+
o = Object.new
|
10
|
+
allow(o).to receive :helpers
|
11
|
+
expect(o).to receive(:helpers).once
|
12
|
+
o
|
13
|
+
end
|
14
|
+
|
15
|
+
it "adds the methods in the Helpers module as helpers to a Sinatra application" do
|
16
|
+
MSFL::Sinatra.registered app
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ".parse_filter_from" do
|
21
|
+
|
22
|
+
subject(:mut) { MSFL::Sinatra.parse_filter_from params }
|
23
|
+
|
24
|
+
let(:params) { { filter: filter } }
|
25
|
+
|
26
|
+
let(:filter) { nil }
|
27
|
+
|
28
|
+
context "when the params[:filter] is a hash" do
|
29
|
+
|
30
|
+
let(:filter) { { foo: inner_array } }
|
31
|
+
|
32
|
+
let(:inner_array) { ["cat", "dog"] }
|
33
|
+
|
34
|
+
let(:expected) { { foo: MSFL::Types::Set.new(inner_array) } }
|
35
|
+
|
36
|
+
it "is the MSFL parsed filter" do
|
37
|
+
expect(mut).to eq expected
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe ".dataset_from" do
|
43
|
+
|
44
|
+
subject(:mut) { MSFL::Sinatra.dataset_from params }
|
45
|
+
|
46
|
+
let(:params) { { dataset: dataset } }
|
47
|
+
|
48
|
+
let(:dataset) { nil }
|
49
|
+
|
50
|
+
context "when params[:dataset] is :movies" do
|
51
|
+
|
52
|
+
let(:dataset) { :movies }
|
53
|
+
|
54
|
+
it "is a new instance of MSFL::Datasets::Movies" do
|
55
|
+
expect(mut).to be_a MSFL::Datasets::Movies
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when params[:dataset] is :cars" do
|
60
|
+
|
61
|
+
let(:dataset) { :cars }
|
62
|
+
|
63
|
+
it "is a new instance of MSFL::Datasets::Cars" do
|
64
|
+
expect(mut).to be_a MSFL::Datasets::Cars
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".validator_from" do
|
70
|
+
|
71
|
+
subject(:mut) { MSFL::Sinatra.validator_from params }
|
72
|
+
|
73
|
+
let(:params) { { dataset: dataset } }
|
74
|
+
|
75
|
+
let(:dataset) { nil }
|
76
|
+
|
77
|
+
context "when params[:dataset] is :movies" do
|
78
|
+
|
79
|
+
let(:dataset) { :movies }
|
80
|
+
|
81
|
+
it "is a semantic validator instance of MSFL::Datasets::Movies" do
|
82
|
+
validator = mut
|
83
|
+
expect(validator).to be_a MSFL::Validators::Semantic
|
84
|
+
expect(validator.dataset).to be_a MSFL::Datasets::Movies
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe ".validate" do
|
90
|
+
|
91
|
+
subject(:mut) { MSFL::Sinatra.validate params }
|
92
|
+
|
93
|
+
let(:params) { nil }
|
94
|
+
|
95
|
+
it_behaves_like "an invocation of MSFL::Sinatra.validate"
|
96
|
+
end
|
97
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,4 +3,6 @@ SimpleCov.start 'msfl'
|
|
3
3
|
require 'rspec/support/spec'
|
4
4
|
require 'byebug'
|
5
5
|
require_relative '../lib/msfl'
|
6
|
-
require_relative 'msfl/shared_examples'
|
6
|
+
require_relative 'msfl/shared_examples'
|
7
|
+
require_relative '../lib/msfl/datasets/movies'
|
8
|
+
require_relative '../lib/msfl/datasets/cars'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.pre.
|
4
|
+
version: 0.0.1.pre.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Courtland Caldwell
|
@@ -112,9 +112,11 @@ files:
|
|
112
112
|
- lib/msfl/configuration.rb
|
113
113
|
- lib/msfl/datasets.rb
|
114
114
|
- lib/msfl/datasets/base.rb
|
115
|
+
- lib/msfl/datasets/cars.rb
|
115
116
|
- lib/msfl/datasets/movies.rb
|
116
117
|
- lib/msfl/parsers.rb
|
117
118
|
- lib/msfl/parsers/json.rb
|
119
|
+
- lib/msfl/sinatra.rb
|
118
120
|
- lib/msfl/types.rb
|
119
121
|
- lib/msfl/types/set.rb
|
120
122
|
- lib/msfl/validators.rb
|
@@ -126,6 +128,8 @@ files:
|
|
126
128
|
- spec/msfl/datasets/base_spec.rb
|
127
129
|
- spec/msfl/parsers/json_spec.rb
|
128
130
|
- spec/msfl/shared_examples.rb
|
131
|
+
- spec/msfl/sinatra/helpers_spec.rb
|
132
|
+
- spec/msfl/sinatra_spec.rb
|
129
133
|
- spec/msfl/validators/semantic_spec.rb
|
130
134
|
- spec/msfl_spec.rb
|
131
135
|
- spec/spec_helper.rb
|
@@ -157,6 +161,8 @@ test_files:
|
|
157
161
|
- spec/msfl/datasets/base_spec.rb
|
158
162
|
- spec/msfl/parsers/json_spec.rb
|
159
163
|
- spec/msfl/shared_examples.rb
|
164
|
+
- spec/msfl/sinatra/helpers_spec.rb
|
165
|
+
- spec/msfl/sinatra_spec.rb
|
160
166
|
- spec/msfl/validators/semantic_spec.rb
|
161
167
|
- spec/msfl_spec.rb
|
162
168
|
- spec/spec_helper.rb
|