better_model 1.3.0 → 2.0.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 +4 -4
- data/README.md +155 -1
- data/lib/better_model/archivable.rb +3 -2
- data/lib/better_model/predicable.rb +73 -52
- data/lib/better_model/schedulable/occurrence_calculator.rb +1034 -0
- data/lib/better_model/schedulable/schedule_builder.rb +269 -0
- data/lib/better_model/schedulable.rb +356 -0
- data/lib/better_model/searchable.rb +4 -4
- data/lib/better_model/version.rb +1 -1
- data/lib/better_model.rb +4 -0
- data/lib/generators/better_model/taggable/taggable_generator.rb +129 -0
- data/lib/generators/better_model/taggable/templates/README.tt +62 -0
- data/lib/generators/better_model/taggable/templates/migration.rb.tt +21 -0
- metadata +8 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/migration"
|
|
5
|
+
|
|
6
|
+
module BetterModel
|
|
7
|
+
module Generators
|
|
8
|
+
class TaggableGenerator < Rails::Generators::NamedBase
|
|
9
|
+
include Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
class_option :column_name, type: :string, default: "tags",
|
|
14
|
+
desc: "Name of the tags column (default: tags)"
|
|
15
|
+
class_option :skip_index, type: :boolean, default: false,
|
|
16
|
+
desc: "Skip adding GIN index (PostgreSQL only)"
|
|
17
|
+
|
|
18
|
+
def self.next_migration_number(dirname)
|
|
19
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
20
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_migration_file
|
|
24
|
+
migration_template "migration.rb.tt",
|
|
25
|
+
"db/migrate/add_#{column_name}_to_#{table_name}.rb"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def show_readme
|
|
29
|
+
# Display README as template to interpolate variables
|
|
30
|
+
if behavior == :invoke
|
|
31
|
+
say "=" * 79, :green
|
|
32
|
+
say ""
|
|
33
|
+
say "Taggable column has been added to your model!"
|
|
34
|
+
say ""
|
|
35
|
+
say "Next steps:"
|
|
36
|
+
say ""
|
|
37
|
+
say "1. Run the migration:"
|
|
38
|
+
say " $ bin/rails db:migrate"
|
|
39
|
+
say ""
|
|
40
|
+
say "2. Add Taggable configuration to your model:"
|
|
41
|
+
say ""
|
|
42
|
+
say " class #{model_name} < ApplicationRecord"
|
|
43
|
+
say " include BetterModel"
|
|
44
|
+
say ""
|
|
45
|
+
if postgresql?
|
|
46
|
+
say " # PostgreSQL: No additional configuration needed"
|
|
47
|
+
else
|
|
48
|
+
say " # SQLite/MySQL: Add serialization for the tags column"
|
|
49
|
+
say " serialize :#{column_name}, coder: JSON, type: Array"
|
|
50
|
+
say ""
|
|
51
|
+
end
|
|
52
|
+
say " taggable do"
|
|
53
|
+
say " tag_field :#{column_name} # Column name (default: :tags)"
|
|
54
|
+
say " normalize true # Convert to lowercase"
|
|
55
|
+
say " strip true # Remove whitespace (default)"
|
|
56
|
+
say " min_length 2 # Minimum tag length"
|
|
57
|
+
say " max_length 30 # Maximum tag length"
|
|
58
|
+
say " validates_tags minimum: 1, maximum: 10"
|
|
59
|
+
say " end"
|
|
60
|
+
say " end"
|
|
61
|
+
say ""
|
|
62
|
+
say "3. Usage examples:"
|
|
63
|
+
say ""
|
|
64
|
+
say " # Add tags"
|
|
65
|
+
say " #{name.underscore}.tag_with(\"ruby\", \"rails\", \"web\")"
|
|
66
|
+
say ""
|
|
67
|
+
say " # Remove tags"
|
|
68
|
+
say " #{name.underscore}.untag(\"web\")"
|
|
69
|
+
say ""
|
|
70
|
+
say " # Replace all tags"
|
|
71
|
+
say " #{name.underscore}.retag(\"ruby\", \"api\")"
|
|
72
|
+
say ""
|
|
73
|
+
say " # Check for tag"
|
|
74
|
+
say " #{name.underscore}.tagged_with?(\"ruby\") # => true"
|
|
75
|
+
say ""
|
|
76
|
+
say " # CSV interface"
|
|
77
|
+
say " #{name.underscore}.tag_list = \"ruby, rails, tutorial\""
|
|
78
|
+
say " #{name.underscore}.tag_list # => \"ruby, rails, tutorial\""
|
|
79
|
+
say ""
|
|
80
|
+
say " # Search with tags (Predicable integration)"
|
|
81
|
+
say " #{model_name}.#{column_name}_contains(\"ruby\")"
|
|
82
|
+
say " #{model_name}.#{column_name}_overlaps([\"ruby\", \"python\"])"
|
|
83
|
+
say " #{model_name}.#{column_name}_contains_all([\"ruby\", \"rails\"])"
|
|
84
|
+
say ""
|
|
85
|
+
say " # Statistics"
|
|
86
|
+
say " #{model_name}.tag_counts # => {\"ruby\" => 45, \"rails\" => 38}"
|
|
87
|
+
say " #{model_name}.popular_tags(limit: 10) # => [[\"ruby\", 45], [\"rails\", 38]]"
|
|
88
|
+
say " #{model_name}.related_tags(\"ruby\", limit: 5) # => [\"rails\", \"gem\", \"tutorial\"]"
|
|
89
|
+
say ""
|
|
90
|
+
say "For more information, see: docs/taggable.md"
|
|
91
|
+
say ""
|
|
92
|
+
say "=" * 79, :green
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def table_name
|
|
99
|
+
name.tableize
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def model_name
|
|
103
|
+
name.camelize
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def migration_class_name
|
|
107
|
+
"Add#{column_name.camelize}To#{name.camelize.pluralize}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def column_name
|
|
111
|
+
options[:column_name]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def add_index?
|
|
115
|
+
!options[:skip_index]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def postgresql?
|
|
119
|
+
# Check if PostgreSQL adapter is being used
|
|
120
|
+
# Note: This checks the current adapter, which may not reflect production
|
|
121
|
+
# The migration template includes runtime detection as well
|
|
122
|
+
return false unless defined?(ActiveRecord::Base)
|
|
123
|
+
|
|
124
|
+
adapter_name = ActiveRecord::Base.connection.adapter_name rescue nil
|
|
125
|
+
adapter_name == "PostgreSQL"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
===============================================================================
|
|
2
|
+
|
|
3
|
+
Taggable column has been added to your model!
|
|
4
|
+
|
|
5
|
+
Next steps:
|
|
6
|
+
|
|
7
|
+
1. Run the migration:
|
|
8
|
+
$ bin/rails db:migrate
|
|
9
|
+
|
|
10
|
+
2. Add Taggable configuration to your model:
|
|
11
|
+
|
|
12
|
+
class <%= model_name %> < ApplicationRecord
|
|
13
|
+
include BetterModel
|
|
14
|
+
|
|
15
|
+
<% if postgresql? -%>
|
|
16
|
+
# PostgreSQL: No additional configuration needed
|
|
17
|
+
<% else -%>
|
|
18
|
+
# SQLite/MySQL: Add serialization for the tags column
|
|
19
|
+
serialize :<%= column_name %>, coder: JSON, type: Array
|
|
20
|
+
|
|
21
|
+
<% end -%>
|
|
22
|
+
taggable do
|
|
23
|
+
tag_field :<%= column_name %> # Column name (default: :tags)
|
|
24
|
+
normalize true # Convert to lowercase
|
|
25
|
+
strip true # Remove whitespace (default)
|
|
26
|
+
min_length 2 # Minimum tag length
|
|
27
|
+
max_length 30 # Maximum tag length
|
|
28
|
+
validates_tags minimum: 1, maximum: 10
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
3. Usage examples:
|
|
33
|
+
|
|
34
|
+
# Add tags
|
|
35
|
+
product.tag_with("ruby", "rails", "web")
|
|
36
|
+
|
|
37
|
+
# Remove tags
|
|
38
|
+
product.untag("web")
|
|
39
|
+
|
|
40
|
+
# Replace all tags
|
|
41
|
+
product.retag("ruby", "api")
|
|
42
|
+
|
|
43
|
+
# Check for tag
|
|
44
|
+
product.tagged_with?("ruby") # => true
|
|
45
|
+
|
|
46
|
+
# CSV interface
|
|
47
|
+
product.tag_list = "ruby, rails, tutorial"
|
|
48
|
+
product.tag_list # => "ruby, rails, tutorial"
|
|
49
|
+
|
|
50
|
+
# Search with tags (Predicable integration)
|
|
51
|
+
<%= model_name %>.<%= column_name %>_contains("ruby")
|
|
52
|
+
<%= model_name %>.<%= column_name %>_overlaps(["ruby", "python"])
|
|
53
|
+
<%= model_name %>.<%= column_name %>_contains_all(["ruby", "rails"])
|
|
54
|
+
|
|
55
|
+
# Statistics
|
|
56
|
+
<%= model_name %>.tag_counts # => {"ruby" => 45, "rails" => 38}
|
|
57
|
+
<%= model_name %>.popular_tags(limit: 10) # => [["ruby", 45], ["rails", 38]]
|
|
58
|
+
<%= model_name %>.related_tags("ruby", limit: 5) # => ["rails", "gem", "tutorial"]
|
|
59
|
+
|
|
60
|
+
For more information, see: docs/taggable.md
|
|
61
|
+
|
|
62
|
+
===============================================================================
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
change_table :<%= table_name %> do |t|
|
|
4
|
+
# Tags column - database-specific implementation
|
|
5
|
+
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
|
6
|
+
# PostgreSQL: Use native array with default empty array
|
|
7
|
+
t.string :<%= column_name %>, array: true, default: []
|
|
8
|
+
else
|
|
9
|
+
# SQLite/MySQL: Use text column (requires serialization in model)
|
|
10
|
+
t.text :<%= column_name %>
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
<% if add_index? -%>
|
|
14
|
+
|
|
15
|
+
# Add index for PostgreSQL array searches (GIN index for better performance)
|
|
16
|
+
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
|
17
|
+
add_index :<%= table_name %>, :<%= column_name %>, using: 'gin'
|
|
18
|
+
end
|
|
19
|
+
<% end -%>
|
|
20
|
+
end
|
|
21
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: better_model
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- alessiobussolari
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -46,6 +46,9 @@ files:
|
|
|
46
46
|
- lib/better_model/permissible.rb
|
|
47
47
|
- lib/better_model/predicable.rb
|
|
48
48
|
- lib/better_model/railtie.rb
|
|
49
|
+
- lib/better_model/schedulable.rb
|
|
50
|
+
- lib/better_model/schedulable/occurrence_calculator.rb
|
|
51
|
+
- lib/better_model/schedulable/schedule_builder.rb
|
|
49
52
|
- lib/better_model/searchable.rb
|
|
50
53
|
- lib/better_model/sortable.rb
|
|
51
54
|
- lib/better_model/state_transition.rb
|
|
@@ -70,6 +73,9 @@ files:
|
|
|
70
73
|
- lib/generators/better_model/stateable/templates/README
|
|
71
74
|
- lib/generators/better_model/stateable/templates/install_migration.rb.tt
|
|
72
75
|
- lib/generators/better_model/stateable/templates/migration.rb.tt
|
|
76
|
+
- lib/generators/better_model/taggable/taggable_generator.rb
|
|
77
|
+
- lib/generators/better_model/taggable/templates/README.tt
|
|
78
|
+
- lib/generators/better_model/taggable/templates/migration.rb.tt
|
|
73
79
|
- lib/generators/better_model/traceable/templates/create_table_migration.rb.tt
|
|
74
80
|
- lib/generators/better_model/traceable/traceable_generator.rb
|
|
75
81
|
- lib/tasks/better_model_tasks.rake
|