searchjoy 0.5.1 → 1.1.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: a837b7470769cd1d7589023ab3f1ffed51b683ea67016d3f642aa26b9edcab3e
4
- data.tar.gz: 54239d751a05741b0b8fbfcedbc6d130af6a6153499ada75e282bfad7acec716
3
+ metadata.gz: d828271eb123a378fe3491da733c10c097f083dcdc9b6586fd3947c032f064b7
4
+ data.tar.gz: e907e0cd11b7c68e9431184add27fe79d3f35f573d221318e25e34a33a99847e
5
5
  SHA512:
6
- metadata.gz: 8f35d980883ded923995f9340867b1685bcb710e66976ce8ce72e18033de94155c49ff1b53b8c4ba2fdb3d4c1f365bd0db8ee8dd0e70de39730e9f8f4124fb36
7
- data.tar.gz: 191ee153cd9941773f2c388b38816513e1319ec7f29f5a6d59ad8d5182e310080d7260a9ebfecc7e4dda8801f61dbaab3576426912848787a713c60ef8c92782
6
+ metadata.gz: fdfc1fd30d7e31156d6891eee806933cead952c6bb0cca8eb1c9579b279b8629809ca08eaa7e01b8035b728ffc3f7c32a769822106c01422577a1c4830d1ab73
7
+ data.tar.gz: c04d0eb16545d44431e962d7bb1e2fb177bd717a53049f0924c565eccc7fe53966c58e8a1eb10cf7656813c6ca9d265825d010a1a1cad15365acdcd8d1c9f69a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 1.1.0 (2023-02-07)
2
+
3
+ - Improved dashboard labels
4
+ - Improved CSP support
5
+ - Dropped support for Ruby < 2.7 and Rails < 6
6
+
7
+ ## 1.0.0 (2022-05-10)
8
+
9
+ - Added support for multiple conversions per search
10
+ - Added experimental support for Propshaft
11
+ - Dropped support for Ruby < 2.6 and Rails < 5.2
12
+
1
13
  ## 0.5.1 (2020-09-07)
2
14
 
3
15
  - Use `datetime` type in migration
@@ -57,3 +69,44 @@ Breaking
57
69
  ## 0.1.0 (2016-08-23)
58
70
 
59
71
  - Fixed Searchkick integration for `execute: false`
72
+
73
+ ## 0.0.10 (2016-04-20)
74
+
75
+ - Fixed error when block passed to `search`
76
+
77
+ ## 0.0.9 (2016-03-12)
78
+
79
+ - Fixed error with routes
80
+
81
+ ## 0.0.8 (2015-12-12)
82
+
83
+ - Added `user_id` to searches
84
+
85
+ ## 0.0.7 (2014-08-18)
86
+
87
+ - Fixed error with Searchkick 0.8
88
+
89
+ ## 0.0.6 (2013-12-19)
90
+
91
+ - Fixed error with Rails 3
92
+
93
+ ## 0.0.5 (2013-11-01)
94
+
95
+ - Renamed to Searchjoy
96
+
97
+ ## 0.0.4 (2013-10-31)
98
+
99
+ - Renamed `converted` to `convert`
100
+
101
+ ## 0.0.3 (2013-10-31)
102
+
103
+ - Added `conversion_name` option
104
+ - Added `converted` method
105
+
106
+ ## 0.0.2 (2013-10-31)
107
+
108
+ - Added dashboard
109
+
110
+ ## 0.0.1 (2013-10-24)
111
+
112
+ - First release
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2020 Andrew Kane
1
+ Copyright (c) 2013-2023 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,17 +4,17 @@ Search analytics made easy
4
4
 
5
5
  [See it in action](https://searchjoy.dokkuapp.com/)
6
6
 
7
- [![Screenshot](https://searchjoy.dokkuapp.com/assets/searchjoy-a9d4c01356a822bc1dba1eecf7ac76387de009fc9902eb9a880057069212be80.png)](https://searchjoy.dokkuapp.com/)
7
+ [![Screenshot](https://searchjoy.dokkuapp.com/assets/searchjoy-7be12d922ca8b31b7d7440e618b0c666698a4b15752653a0c5c45e3dd2737142.png)](https://searchjoy.dokkuapp.com/)
8
8
 
9
9
  - view searches in real-time
10
- - track conversions week over week
10
+ - track conversion rate week over week
11
11
  - monitor the performance of top searches
12
12
 
13
- Works with any search platform, including Elasticsearch, Sphinx, and Solr
13
+ Works with any search platform, including Elasticsearch, OpenSearch, Sphinx, and Solr
14
14
 
15
15
  :cupid: An amazing companion to [Searchkick](https://github.com/ankane/searchkick)
16
16
 
17
- [![Build Status](https://travis-ci.org/ankane/searchjoy.svg?branch=master)](https://travis-ci.org/ankane/searchjoy)
17
+ [![Build Status](https://github.com/ankane/searchjoy/workflows/build/badge.svg?branch=master)](https://github.com/ankane/searchjoy/actions)
18
18
 
19
19
  ## Installation
20
20
 
@@ -70,8 +70,6 @@ Then, pass the values to the `track` option.
70
70
  Item.search("apple", track: {user_id: 1, source: "web"})
71
71
  ```
72
72
 
73
- It’s that easy.
74
-
75
73
  ## Track Conversions
76
74
 
77
75
  First, choose a conversion metric. At Instacart, an item added to the cart from the search results page counts as a conversion.
@@ -121,16 +119,29 @@ ENV["SEARCHJOY_PASSWORD"] = "secret"
121
119
 
122
120
  ## Data Retention
123
121
 
124
- Delete older data with:
122
+ Data should only be retained for as long as it’s needed. Delete older data with:
123
+
124
+ ```ruby
125
+ Searchjoy::Search.where("created_at < ?", 1.year.ago).find_in_batches do |searches|
126
+ search_ids = searches.map(&:id)
127
+ Searchjoy::Conversion.where(search_id: search_ids).delete_all
128
+ Searchjoy::Search.where(id: search_ids).delete_all
129
+ end
130
+ ```
131
+
132
+ You can use [Rollup](https://github.com/ankane/rollup) to aggregate important data before you do.
125
133
 
126
134
  ```ruby
127
- Searchjoy::Search.where("created_at < ?", 1.year.ago).in_batches.delete_all
135
+ Searchjoy::Search.rollup("Searches")
128
136
  ```
129
137
 
130
138
  Delete data for a specific user with:
131
139
 
132
140
  ```ruby
133
- Searchjoy::Search.where(user_id: 1).delete_all
141
+ user_id = 123
142
+ search_ids = Searchjoy::Search.where(user_id: user_id).pluck(:id)
143
+ Searchjoy::Conversion.where(search_id: search_ids).delete_all
144
+ Searchjoy::Search.where(id: search_ids).delete_all
134
145
  ```
135
146
 
136
147
  ## Customize
@@ -167,6 +178,46 @@ Show the conversion name in the live stream
167
178
  Searchjoy.conversion_name = ->(model) { model.name }
168
179
  ```
169
180
 
181
+ ## Upgrading
182
+
183
+ ### 1.0
184
+
185
+ Searchjoy now supports multiple conversions per search :tada:
186
+
187
+ Before updating the gem, create a migration with:
188
+
189
+ ```ruby
190
+ create_table :searchjoy_conversions do |t|
191
+ t.references :search
192
+ t.references :convertable, polymorphic: true, index: {name: "index_searchjoy_conversions_on_convertable"}
193
+ t.datetime :created_at
194
+ end
195
+ ```
196
+
197
+ Deploy and run the migration, then update the gem.
198
+
199
+ You can optionally backfill the conversions table
200
+
201
+ ```ruby
202
+ Searchjoy.backfill_conversions
203
+ ```
204
+
205
+ And optionally remove `convertable` from searches
206
+
207
+ ```ruby
208
+ remove_reference :searchjoy_searches, :convertable, polymorphic: true
209
+ ```
210
+
211
+ You can stay with single conversions (and skip all the previous steps) by creating an initializer with:
212
+
213
+ ```ruby
214
+ Searchjoy.multiple_conversions = false
215
+ ```
216
+
217
+ ## History
218
+
219
+ View the [changelog](https://github.com/ankane/searchjoy/blob/master/CHANGELOG.md)
220
+
170
221
  ## Contributing
171
222
 
172
223
  Everyone is encouraged to help improve this project. Here are a few ways you can help:
@@ -23,23 +23,23 @@ module Searchjoy
23
23
  duration = @time_range.last - @time_range.first
24
24
  period =
25
25
  if duration < 3.days # shows 48-72 data points (ends at current time)
26
- "hour"
26
+ :hour
27
27
  elsif duration < 60.days
28
- "day"
28
+ :day
29
29
  elsif duration < 60.weeks # to make it easy to compare to same time last year
30
- "week"
30
+ :week
31
31
  else
32
- "month"
32
+ :month
33
33
  end
34
34
 
35
35
  relation = Searchjoy::Search.where(search_type: params[:search_type])
36
36
  @searches_by_week = relation.group_by_period(period, :created_at, time_zone: @time_zone, range: @time_range).count
37
- @conversions_by_week = relation.where("converted_at is not null").group_by_period(period, :created_at, time_zone: @time_zone, range: @time_range).count
37
+ @converted_by_week = relation.where.not(converted_at: nil).group_by_period(period, :created_at, time_zone: @time_zone, range: @time_range).count
38
38
  @top_searches = @searches.first(5)
39
39
  @bad_conversion_rate = @searches.sort_by { |s| [s["conversion_rate"].to_f, s["query"]] }.first(5).select { |s| s["conversion_rate"] < 50 }
40
40
  @conversion_rate_by_week = {}
41
41
  @searches_by_week.each do |week, searches_count|
42
- @conversion_rate_by_week[week] = searches_count > 0 ? (100.0 * @conversions_by_week[week] / searches_count).round : 0
42
+ @conversion_rate_by_week[week] = searches_count > 0 ? (100.0 * @converted_by_week[week] / searches_count).round : 0
43
43
  end
44
44
  end
45
45
 
@@ -47,7 +47,12 @@ module Searchjoy
47
47
  end
48
48
 
49
49
  def recent
50
- @searches = Searchjoy::Search.includes(:convertable).order("created_at desc").limit(50)
50
+ @searches = Searchjoy::Search.order(created_at: :desc).limit(50)
51
+ if Searchjoy.multiple_conversions
52
+ @searches.includes!(conversions: :convertable)
53
+ else
54
+ @searches.includes!(:convertable)
55
+ end
51
56
  render layout: false
52
57
  end
53
58
 
@@ -81,9 +86,16 @@ module Searchjoy
81
86
 
82
87
  def set_searches
83
88
  @limit = params[:limit] || Searchjoy.top_searches
84
- @searches = Searchjoy::Search.connection.select_all(Searchjoy::Search.select("normalized_query, COUNT(*) as searches_count, COUNT(converted_at) as conversions_count, AVG(results_count) as avg_results_count").where(created_at: @time_range, search_type: @search_type).group("normalized_query").order("searches_count desc, normalized_query asc").limit(@limit).to_sql).to_a
89
+ relation = Searchjoy::Search
90
+ .select("normalized_query, COUNT(*) as searches_count, COUNT(converted_at) as converted_count, AVG(results_count) as avg_results_count")
91
+ .where(created_at: @time_range, search_type: @search_type)
92
+ .group(:normalized_query)
93
+ .order(searches_count: :desc, normalized_query: :asc)
94
+ .limit(@limit)
95
+ # get array of hashes for performance
96
+ @searches = Searchjoy::Search.connection.select_all(relation.to_sql).to_a
85
97
  @searches.each do |search|
86
- search["conversion_rate"] = 100 * search["conversions_count"].to_i / search["searches_count"].to_f
98
+ search["conversion_rate"] = 100 * search["converted_count"].to_i / search["searches_count"].to_f
87
99
  end
88
100
  end
89
101
  end
@@ -0,0 +1,8 @@
1
+ module Searchjoy
2
+ class Conversion < ActiveRecord::Base
3
+ self.table_name = "searchjoy_conversions"
4
+
5
+ belongs_to :search
6
+ belongs_to :convertable, polymorphic: true, optional: true
7
+ end
8
+ end
@@ -4,14 +4,34 @@ module Searchjoy
4
4
 
5
5
  belongs_to :convertable, polymorphic: true, optional: true
6
6
  belongs_to :user, optional: true
7
+ has_many :conversions
7
8
 
8
9
  before_save :set_normalized_query
9
10
 
10
11
  def convert(convertable = nil)
11
- unless converted?
12
- self.converted_at = Time.now
13
- self.convertable = convertable
14
- save(validate: false)
12
+ return unless Searchjoy.multiple_conversions || !converted?
13
+
14
+ # use transaction to keep consistent
15
+ self.class.transaction do
16
+ # make time consistent
17
+ now = Time.now
18
+
19
+ if Searchjoy.multiple_conversions
20
+ conversion =
21
+ conversions.create!(
22
+ convertable: convertable,
23
+ created_at: now
24
+ )
25
+ end
26
+
27
+ unless converted?
28
+ self.converted_at = now
29
+ # check id instead of association
30
+ self.convertable = convertable if respond_to?(:convertable_id=)
31
+ save(validate: false)
32
+ end
33
+
34
+ conversion
15
35
  end
16
36
  end
17
37
 
@@ -4,6 +4,7 @@
4
4
  <title>Searchjoy</title>
5
5
 
6
6
  <meta charset="utf-8" />
7
+ <%= csp_meta_tag %>
7
8
 
8
9
  <style>
9
10
  body {
@@ -288,7 +289,11 @@
288
289
  }
289
290
  </style>
290
291
 
291
- <%= javascript_include_tag "searchjoy/application" %>
292
+ <% if defined?(Propshaft::Railtie) %>
293
+ <%= javascript_include_tag "chartkick", "Chart.bundle", "searchjoy/litepicker", "searchjoy/application", nonce: true %>
294
+ <% else %>
295
+ <%= javascript_include_tag "searchjoy/application", nonce: true %>
296
+ <% end %>
292
297
  </head>
293
298
  <body>
294
299
  <div class="container">
@@ -310,7 +315,7 @@
310
315
  <%= @time_range.first.strftime("%b %-e, %Y") %> to <%= @time_range.last.strftime("%b %-e, %Y") %>
311
316
  </span>
312
317
  <span class="text-muted"><%= @time_zone.name.sub(" (US & Canada)", "") %></span>
313
- <script>
318
+ <%= javascript_tag nonce: true do %>
314
319
  var startDate = <%= raw json_escape(@time_range.first.to_json) %>;
315
320
  var endDate = <%= raw json_escape(@time_range.last.to_json) %>;
316
321
  new Litepicker({
@@ -333,7 +338,7 @@
333
338
  window.location.href = window.location.pathname + "?" + params.toString();
334
339
  }
335
340
  });
336
- </script>
341
+ <% end %>
337
342
  <% end %>
338
343
  </div>
339
344
  </div>
@@ -8,7 +8,7 @@
8
8
  <tr>
9
9
  <th>Query</th>
10
10
  <th class="num" style="width: 20%;"><%= link_to "Searches", searches_path(search_type: params[:search_type], **@time_params), class: ("active" if params[:sort] != "conversion_rate") %></th>
11
- <th class="num" style="width: 20%;">Conversions</th>
11
+ <th class="num" style="width: 20%;">Converted</th>
12
12
  <th class="num" style="width: 20%;"><%= link_to "%", searches_path(search_type: params[:search_type], sort: "conversion_rate", **@time_params), class: ("active" if params[:sort] == "conversion_rate") %></th>
13
13
  <th class="num" style="width: 20%;">Avg Results</th>
14
14
  </tr>
@@ -18,7 +18,7 @@
18
18
  <tr>
19
19
  <td><%= search["normalized_query"] %></td>
20
20
  <td class="num"><%= search["searches_count"] %></td>
21
- <td class="num"><%= search["conversions_count"] %></td>
21
+ <td class="num"><%= search["converted_count"] %></td>
22
22
  <td class="num"><%= number_to_percentage search["conversion_rate"].to_f, precision: 0 %></td>
23
23
  <td class="num"><%= search["avg_results_count"].to_f.round %></td>
24
24
  </tr>
@@ -23,7 +23,7 @@
23
23
  <table>
24
24
  <thead>
25
25
  <tr>
26
- <th>Low Conversions</th>
26
+ <th>Low Conversion Rate</th>
27
27
  <th class="num"><%= link_to "View all", searches_path(search_type: @search_type, sort: "conversion_rate", **@time_params) %></th>
28
28
  </tr>
29
29
  </thead>
@@ -45,4 +45,4 @@
45
45
 
46
46
  <h2>Volume</h2>
47
47
 
48
- <%= line_chart [{name: "Searches", data: @searches_by_week}, {name: "Conversions", data: @conversions_by_week}] %>
48
+ <%= line_chart [{name: "Searches", data: @searches_by_week}, {name: "Converted", data: @converted_by_week}] %>
@@ -15,7 +15,8 @@
15
15
  <td style="width: 35%; color: #5cb85c;">
16
16
  <% if search.converted_at %>
17
17
  <strong>✓</strong>
18
- <%= search.convertable ? (Searchjoy.conversion_name ? Searchjoy.conversion_name.call(search.convertable) : "#{search.convertable_type} #{search.convertable_id}") : "Converted" %>
18
+ <% convertable = Searchjoy.multiple_conversions ? search.conversions.min_by(&:created_at)&.convertable : search.convertable %>
19
+ <%= convertable ? (Searchjoy.conversion_name ? Searchjoy.conversion_name.call(convertable) : "#{convertable.model_name.name} #{convertable.id}") : "Converted" %>
19
20
  <% end %>
20
21
  </td>
21
22
  <td style="width: 20%;" class="num">
@@ -2,7 +2,7 @@
2
2
 
3
3
  <table id="stream"></table>
4
4
 
5
- <script>
5
+ <%= javascript_tag nonce: true do %>
6
6
  function load(element, path) {
7
7
  var request = new XMLHttpRequest();
8
8
  request.open("GET", path, true);
@@ -16,8 +16,8 @@
16
16
  }
17
17
 
18
18
  function fetchRecentSearches() {
19
- load(document.getElementById("stream"), "<%= searches_recent_path %>")
19
+ load(document.getElementById("stream"), <%= raw json_escape(searches_recent_path.to_json) %>)
20
20
  setTimeout(fetchRecentSearches, 5 * 1000);
21
21
  }
22
22
  fetchRecentSearches();
23
- </script>
23
+ <% end %>
@@ -7,12 +7,17 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
7
7
  t.string :normalized_query
8
8
  t.integer :results_count
9
9
  t.datetime :created_at
10
- t.references :convertable, polymorphic: true, index: {name: "index_searchjoy_searches_on_convertable"}
11
10
  t.datetime :converted_at
12
11
  end
13
12
 
14
13
  add_index :searchjoy_searches, [:created_at]
15
14
  add_index :searchjoy_searches, [:search_type, :created_at]
16
15
  add_index :searchjoy_searches, [:search_type, :normalized_query, :created_at], name: "index_searchjoy_searches_on_search_type_query" # autogenerated name is too long
16
+
17
+ create_table :searchjoy_conversions do |t|
18
+ t.references :search
19
+ t.references :convertable, polymorphic: true, index: {name: "index_searchjoy_conversions_on_convertable"}
20
+ t.datetime :created_at
21
+ end
17
22
  end
18
23
  end
@@ -4,7 +4,7 @@ module Searchjoy
4
4
 
5
5
  initializer "searchjoy" do |app|
6
6
  if app.config.respond_to?(:assets)
7
- if defined?(Sprockets) && Sprockets::VERSION >= "4"
7
+ if defined?(Sprockets) && Sprockets::VERSION.to_i >= 4
8
8
  app.config.assets.precompile << "searchjoy/application.js"
9
9
  else
10
10
  # use a proc instead of a string
@@ -1,3 +1,3 @@
1
1
  module Searchjoy
2
- VERSION = "0.5.1"
2
+ VERSION = "1.1.0"
3
3
  end
data/lib/searchjoy.rb CHANGED
@@ -4,8 +4,8 @@ require "chartkick"
4
4
  require "groupdate"
5
5
 
6
6
  # modules
7
- require "searchjoy/track"
8
- require "searchjoy/version"
7
+ require_relative "searchjoy/track"
8
+ require_relative "searchjoy/version"
9
9
 
10
10
  module Searchjoy
11
11
  # time zone
@@ -23,15 +23,34 @@ module Searchjoy
23
23
  mattr_accessor :query_name
24
24
  mattr_accessor :query_url
25
25
 
26
+ # multiple conversions
27
+ mattr_accessor :multiple_conversions
28
+ self.multiple_conversions = true
29
+
26
30
  def self.attach_to_searchkick!
27
31
  Searchkick::Query.prepend(Searchjoy::Track::Query)
28
32
  Searchkick::MultiSearch.prepend(Searchjoy::Track::MultiSearch)
29
33
  Searchkick::Results.send(:attr_accessor, :search)
30
34
  end
35
+
36
+ def self.backfill_conversions
37
+ Searchjoy::Search.where.not(converted_at: nil).left_joins(:conversions).where(searchjoy_conversions: {id: nil}).find_in_batches do |searches|
38
+ conversions =
39
+ searches.map do |search|
40
+ {
41
+ search_id: search.id,
42
+ convertable_id: search.convertable_id,
43
+ convertable_type: search.convertable_type,
44
+ created_at: search.converted_at
45
+ }
46
+ end
47
+ Searchjoy::Conversion.insert_all(conversions)
48
+ end
49
+ end
31
50
  end
32
51
 
33
52
  if defined?(Rails)
34
- require "searchjoy/engine"
35
- else
36
- Searchjoy.attach_to_searchkick! if defined?(Searchkick)
53
+ require_relative "searchjoy/engine"
54
+ elsif defined?(Searchkick)
55
+ Searchjoy.attach_to_searchkick!
37
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchjoy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-08 00:00:00.000000000 Z
11
+ date: 2023-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chartkick
@@ -44,86 +44,16 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '5'
47
+ version: '6'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '5'
55
- - !ruby/object:Gem::Dependency
56
- name: bundler
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: minitest
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: sqlite3
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: searchkick
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'
125
- description:
126
- email: andrew@chartkick.com
54
+ version: '6'
55
+ description:
56
+ email: andrew@ankane.org
127
57
  executables: []
128
58
  extensions: []
129
59
  extra_rdoc_files: []
@@ -134,6 +64,7 @@ files:
134
64
  - app/assets/javascripts/searchjoy/application.js
135
65
  - app/assets/javascripts/searchjoy/litepicker.js
136
66
  - app/controllers/searchjoy/searches_controller.rb
67
+ - app/models/searchjoy/conversion.rb
137
68
  - app/models/searchjoy/search.rb
138
69
  - app/views/layouts/searchjoy/application.html.erb
139
70
  - app/views/searchjoy/searches/index.html.erb
@@ -152,7 +83,7 @@ homepage: https://github.com/ankane/searchjoy
152
83
  licenses:
153
84
  - MIT
154
85
  metadata: {}
155
- post_install_message:
86
+ post_install_message:
156
87
  rdoc_options: []
157
88
  require_paths:
158
89
  - lib
@@ -160,15 +91,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
91
  requirements:
161
92
  - - ">="
162
93
  - !ruby/object:Gem::Version
163
- version: '2.4'
94
+ version: '2.7'
164
95
  required_rubygems_version: !ruby/object:Gem::Requirement
165
96
  requirements:
166
97
  - - ">="
167
98
  - !ruby/object:Gem::Version
168
99
  version: '0'
169
100
  requirements: []
170
- rubygems_version: 3.1.2
171
- signing_key:
101
+ rubygems_version: 3.4.6
102
+ signing_key:
172
103
  specification_version: 4
173
104
  summary: Search analytics made easy
174
105
  test_files: []