rokaki 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa778301c8cf01a3b8743a3a6c42e3dba3412e914138915c08e2a19879695b85
4
- data.tar.gz: f41cb7515c3334f4182109fb1f159558a6ee5ad5b78bdfdd201de73e507ac155
3
+ metadata.gz: 732268408b0eaebe95dc53cfd1cb3788c10d198bb47eddb3f4c75d0ff796d98c
4
+ data.tar.gz: d698e633dd2d8b48ee086c37e35121c870bc16c40a43070f8c8a5f1db04fc8fd
5
5
  SHA512:
6
- metadata.gz: d44fec91020ac46a37df80aced0f349e9827fcdb9bc918c010464c9df8a8a792e9abf037839db1936984ce3723801dc218887482eb246fad34b1524db4c3675b
7
- data.tar.gz: 07b29041a2550fe70ab710dda94e637dab627c7210b2f740f1b9e655b0a4ec6058d58557af7bf71c7dae4c298f1f5046a4d6d6710e4c742f4eaef4a3a803d473
6
+ metadata.gz: f2d9eb53031c10422b3133063792d99f534f4e47ef12fcabe61e52f244d221dae997c437665dada3b7036e017fdd6245f24f54b3507a4ee30dfa0ba976cb8c1e
7
+ data.tar.gz: e632c271dfc09265b2717e348b7becf7fdf401c1921e34edc6cdae67b52cfc0eeebe481815bd608695236cdfb0008c7a6c662b93c89bb605d8ca10fa8cfa2f37
data/Gemfile.lock CHANGED
@@ -6,7 +6,19 @@ PATH
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ activemodel (6.0.0)
10
+ activesupport (= 6.0.0)
11
+ activerecord (6.0.0)
12
+ activemodel (= 6.0.0)
13
+ activesupport (= 6.0.0)
14
+ activesupport (6.0.0)
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ i18n (>= 0.7, < 2)
17
+ minitest (~> 5.1)
18
+ tzinfo (~> 1.1)
19
+ zeitwerk (~> 2.1, >= 2.1.8)
9
20
  coderay (1.1.2)
21
+ concurrent-ruby (1.1.5)
10
22
  diff-lcs (1.3)
11
23
  ffi (1.11.1)
12
24
  formatador (0.2.5)
@@ -24,12 +36,15 @@ GEM
24
36
  guard (~> 2.1)
25
37
  guard-compat (~> 1.1)
26
38
  rspec (>= 2.99.0, < 4.0)
39
+ i18n (1.6.0)
40
+ concurrent-ruby (~> 1.0)
27
41
  listen (3.1.5)
28
42
  rb-fsevent (~> 0.9, >= 0.9.4)
29
43
  rb-inotify (~> 0.9, >= 0.9.7)
30
44
  ruby_dep (~> 1.2)
31
45
  lumberjack (1.0.13)
32
46
  method_source (0.9.2)
47
+ minitest (5.11.3)
33
48
  nenv (0.3.0)
34
49
  notiffany (0.1.3)
35
50
  nenv (~> 0.1)
@@ -56,12 +71,18 @@ GEM
56
71
  rspec-support (3.8.2)
57
72
  ruby_dep (1.5.0)
58
73
  shellany (0.0.1)
74
+ sqlite3 (1.4.1)
59
75
  thor (0.20.3)
76
+ thread_safe (0.3.6)
77
+ tzinfo (1.2.5)
78
+ thread_safe (~> 0.1)
79
+ zeitwerk (2.1.9)
60
80
 
61
81
  PLATFORMS
62
82
  ruby
63
83
 
64
84
  DEPENDENCIES
85
+ activerecord
65
86
  bundler (~> 2.0)
66
87
  guard
67
88
  guard-rspec
@@ -69,6 +90,7 @@ DEPENDENCIES
69
90
  rake (~> 10.0)
70
91
  rokaki!
71
92
  rspec (~> 3.0)
93
+ sqlite3
72
94
 
73
95
  BUNDLED WITH
74
96
  2.0.2
data/README.md CHANGED
@@ -9,17 +9,13 @@ It's a simple gem that just provides you with a basic dsl based on the filter pa
9
9
  Add this line to your application's Gemfile:
10
10
 
11
11
  ```ruby
12
- gem 'rokaki'
12
+ gem 'rokaki', git: 'https://github.com/tevio/rokaki.git'
13
13
  ```
14
14
 
15
15
  And then execute:
16
16
 
17
17
  $ bundle
18
18
 
19
- Or install it yourself as:
20
-
21
- $ gem install rokaki
22
-
23
19
  ## Usage
24
20
 
25
21
  To use the basic DSL include the `Rokaki::Filterable` module
@@ -45,7 +41,7 @@ A simple example might be:-
45
41
  end
46
42
  ```
47
43
 
48
- This would map attributes `date`, `author_first_name` and `author_last_name`, from a filters object with the structure `{ date: '10-10-10', author: { first_name: 'Shteeve' } }`.
44
+ This maps attributes `date`, `author_first_name` and `author_last_name` to a filters object with the structure `{ date: '10-10-10', author: { first_name: 'Shteeve' } }`.
49
45
 
50
46
  ## Additional options
51
47
  You can specify a `filter_key_prefix` and a `filter_key_infix` to change the structure of the accessors.
@@ -53,6 +49,41 @@ You can specify a `filter_key_prefix` and a `filter_key_infix` to change the str
53
49
  `filter_key_prefix :__` would result in key accessors like `__author_first_name`
54
50
  `filter_key_infix :__` would result in key accessors like `author__first_name`
55
51
 
52
+ ### ActiveRecord
53
+ Include `Rokaki::FilterModel` in any ActiveRecord model (only AR >= 6.0.0 tested so far) you can generate the filter keys and the actual filter lookup code using the `filters` keyword on a model like so:-
54
+
55
+ ```
56
+ # Given the models
57
+ class Author < ActiveRecord::Base
58
+ has_many :articles, inverse_of: :author
59
+ end
60
+
61
+ class Article < ActiveRecord::Base
62
+ belongs_to :author, inverse_of: :articles, required: true
63
+ end
64
+
65
+
66
+ class ArticleFilter
67
+ include FilterModel
68
+
69
+ filters :date, :title, author: [:first_name, :last_name]
70
+
71
+ attr_accessor :filters
72
+
73
+ def initialize(filters:, model: Article)
74
+ @filters = filters
75
+ @model = model
76
+ end
77
+ end
78
+
79
+ filter = ArticleFilter.new(filters: params[:filters])
80
+
81
+ filtered_results = filter.results
82
+
83
+ * note: This will currently return full text matches (I hope to add partial matching with like and ilike soon)
84
+ ```
85
+
86
+
56
87
  ## Development
57
88
 
58
89
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -61,7 +92,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
61
92
 
62
93
  ## Contributing
63
94
 
64
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rokaki. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
95
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tevio/rokaki. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
65
96
 
66
97
  ## License
67
98
 
@@ -69,4 +100,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
69
100
 
70
101
  ## Code of Conduct
71
102
 
72
- Everyone interacting in the Rokaki project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rokaki/blob/master/CODE_OF_CONDUCT.md).
103
+ Everyone interacting in the Rokaki project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/tevio/rokaki/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,114 @@
1
+ module Rokaki
2
+ module FilterModel
3
+ include Filterable
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ include Filterable::ClassMethods
10
+ private
11
+
12
+ def filters(*filter_keys)
13
+ define_filter_keys *filter_keys
14
+
15
+ @_chain_filters = []
16
+ filter_keys.each do |filter_key|
17
+ _chain_filter(filter_key) unless filter_key.is_a? Hash
18
+ _chain_nested filter_key if filter_key.is_a? Hash
19
+ end
20
+
21
+ define_results
22
+ end
23
+
24
+ def _chain_filter(key)
25
+ @_chain_filters << "@model = @model.where(#{key.to_s}: #{key.to_s}) if #{key.to_s};"
26
+ end
27
+
28
+ def _build_deep_chain(keys)
29
+ name = @filter_key_prefix.to_s
30
+ count = keys.size - 1
31
+
32
+ joins = ""
33
+ where = ""
34
+ out = ""
35
+
36
+ leaf = keys.pop
37
+
38
+ keys.each_with_index do |key, i|
39
+ if keys.length == 1
40
+ name = "#{key}#{filter_key_infix.to_s}#{leaf}"
41
+ joins = ":#{key}"
42
+
43
+ where = "{ #{key.to_s.pluralize}: { #{leaf}: #{name} } }"
44
+ end
45
+ end
46
+
47
+ # keys.each_with_index do |key, i|
48
+ # name += key.to_s
49
+ # name += filter_key_infix.to_s unless count == i
50
+
51
+ # joins += "{ #{key.to_s}: " unless count == i
52
+ # where += "{ #{key.to_s.pluralize}: " unless count == i
53
+ # out += " }" unless count == i
54
+
55
+ # joins += key.to_s if count == i
56
+ # where += name if count == i
57
+ # end
58
+
59
+ joins = joins += out
60
+ where = where += out
61
+
62
+ @_chain_filters << "@model = @model.joins(#{joins}).where(#{where}) if #{name};"
63
+ end
64
+
65
+ def _chain_nested(filters_object)
66
+ filters_object.keys.each do |key|
67
+ deep_chain([key], filters_object[key])
68
+ end
69
+ end
70
+
71
+ def associated_table(association)
72
+ @model.reflect_on_association(association).klass.table_name
73
+ end
74
+
75
+ def filter_model(model)
76
+ @model = model
77
+ end
78
+
79
+ def like(*args)
80
+
81
+ end
82
+
83
+ def deep_chain(keys, value)
84
+ if value.is_a? Hash
85
+ value.keys.map do |key|
86
+ _keys = keys.dup << key
87
+ deep_chain(_keys, value[key])
88
+ end
89
+ end
90
+
91
+ if value.is_a? Array
92
+ value.each do |av|
93
+ _keys = keys.dup << av
94
+ _build_deep_chain(_keys)
95
+ end
96
+ end
97
+
98
+ if value.is_a? Symbol
99
+ _keys = keys.dup << value
100
+ _build_deep_chain(_keys)
101
+ end
102
+ end
103
+
104
+ def define_results
105
+ results_def = "def results;"
106
+ @_chain_filters.each do |item|
107
+ results_def += item
108
+ end
109
+ results_def += '@model;end;'
110
+ class_eval results_def
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,76 @@
1
+ module Rokaki
2
+ module Filterable
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ # class methods such as define_filter_keys which comprise the dsl
8
+ #
9
+ module ClassMethods
10
+ private
11
+
12
+ def define_filter_keys(*filter_keys)
13
+ filter_keys.each do |filter_key|
14
+ _build_filter([filter_key]) unless filter_key.is_a? Hash
15
+ _nested_key filter_key if filter_key.is_a? Hash
16
+ end
17
+ end
18
+
19
+ def filter_key_prefix(prefix)
20
+ @filter_key_prefix ||= prefix
21
+ end
22
+
23
+ def filter_key_infix(infix = :_)
24
+ @filter_key_infix ||= infix
25
+ end
26
+
27
+ def _build_filter(keys)
28
+ name = @filter_key_prefix.to_s
29
+ filters = "filters"
30
+ count = keys.size - 1
31
+
32
+ keys.each_with_index do |key, i|
33
+ name += key.to_s
34
+ name += filter_key_infix.to_s unless count == i
35
+
36
+ filters += "[:#{key}]"
37
+ end
38
+
39
+ class_eval "def #{name}; filters.dig(*#{keys}); end;", __FILE__, __LINE__
40
+ end
41
+
42
+ def _nested_key(filters_object)
43
+ filters_object.keys.each do |key|
44
+ deep_map([key], filters_object[key])
45
+ end
46
+ end
47
+
48
+ def deep_map(keys, value)
49
+ if value.is_a? Hash
50
+ value.keys.map do |key|
51
+ _keys = keys.dup << key
52
+ deep_map(_keys, value[key])
53
+ end
54
+ end
55
+
56
+ if value.is_a? Array
57
+ value.each do |av|
58
+ _keys = keys.dup << av
59
+ _build_filter(_keys)
60
+ end
61
+ end
62
+
63
+ if value.is_a? Symbol
64
+ _keys = keys.dup << value
65
+ _build_filter(_keys)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ def filters
72
+ raise Error, "Filterable object must implement 'filters' method that returns a hash"
73
+ end
74
+
75
+ end
76
+ end
@@ -1,3 +1,3 @@
1
1
  module Rokaki
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/rokaki.rb CHANGED
@@ -1,84 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rokaki/version'
4
+ require 'rokaki/filterable'
5
+ require 'rokaki/filter_model'
4
6
 
5
7
  module Rokaki
6
8
  class Error < StandardError; end
7
9
 
8
10
  # include this module for filters dsl in an object
9
11
  #
10
- module Filterable
11
- def self.included(base)
12
- base.extend(ClassMethods)
13
- end
14
-
15
- # class methods such as define_filter_keys which comprise the dsl
16
- #
17
- module ClassMethods
18
- private
19
-
20
- def define_filter_keys(*filter_keys)
21
- filter_keys.each do |filter_key|
22
- _build_filter([filter_key]) unless filter_key.is_a? Hash
23
- _nested_key filter_key if filter_key.is_a? Hash
24
- end
25
- end
26
-
27
- def filter_key_prefix(prefix)
28
- @filter_key_prefix ||= prefix
29
- end
30
-
31
- def filter_key_infix(infix = :_)
32
- @filter_key_infix ||= infix
33
- end
34
-
35
- def _build_filter(keys)
36
- name = @filter_key_prefix.to_s
37
- filters = "filters"
38
- count = keys.size - 1
39
-
40
- keys.each_with_index do |key, i|
41
- name += key.to_s
42
- name += filter_key_infix.to_s unless count == i
43
-
44
- filters += "[:#{key}]"
45
- end
46
-
47
- class_eval "def #{name}; #{filters}; end;", __FILE__, __LINE__
48
- end
49
-
50
- def _nested_key(filters_object)
51
- filters_object.keys.each do |key|
52
- deep_map([key], filters_object[key])
53
- end
54
- end
55
-
56
- def deep_map(keys, value)
57
- if value.is_a? Hash
58
- value.keys.map do |key|
59
- _keys = keys.dup << key
60
- deep_map(_keys, value[key])
61
- end
62
- end
63
-
64
- if value.is_a? Array
65
- value.each do |av|
66
- _keys = keys.dup << av
67
- _build_filter(_keys)
68
- end
69
- end
70
-
71
- if value.is_a? Symbol
72
- _keys = keys.dup << value
73
- _build_filter(_keys)
74
- end
75
- end
76
-
77
- end
78
-
79
- def filters
80
- raise Error, "Filterable object must implement 'filters' method that returns a hash"
81
- end
82
-
83
- end
84
12
  end
data/rokaki.gemspec CHANGED
@@ -10,12 +10,12 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = %q{A web request filtering library}
12
12
  spec.description = %q{A dsl for filtering data in web requests}
13
- # spec.homepage = "Rokaki"
13
+ spec.homepage = "https://github.com/tevio/rokaki"
14
14
  spec.license = "MIT"
15
15
 
16
16
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
17
 
18
- # spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
19
  # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
20
20
  # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
21
21
 
@@ -34,4 +34,6 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "guard"
35
35
  spec.add_development_dependency "pry"
36
36
  spec.add_development_dependency "guard-rspec"
37
+ spec.add_development_dependency "activerecord"
38
+ spec.add_development_dependency "sqlite3"
37
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rokaki
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Martin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-01 00:00:00.000000000 Z
11
+ date: 2019-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activerecord
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  description: A dsl for filtering data in web requests
98
126
  email:
99
127
  - steve@martian.media
@@ -114,12 +142,15 @@ files:
114
142
  - bin/console
115
143
  - bin/setup
116
144
  - lib/rokaki.rb
145
+ - lib/rokaki/filter_model.rb
146
+ - lib/rokaki/filterable.rb
117
147
  - lib/rokaki/version.rb
118
148
  - rokaki.gemspec
119
- homepage:
149
+ homepage: https://github.com/tevio/rokaki
120
150
  licenses:
121
151
  - MIT
122
- metadata: {}
152
+ metadata:
153
+ homepage_uri: https://github.com/tevio/rokaki
123
154
  post_install_message:
124
155
  rdoc_options: []
125
156
  require_paths: