rokaki 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: