yard-is-sequel 0.8.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 +7 -0
- data/LICENSE +674 -0
- data/README.md +245 -0
- data/lib/yard-is-sequel/associations.rb +211 -0
- data/lib/yard-is-sequel/fields.rb +79 -0
- data/lib/yard-is-sequel/info.rb +16 -0
- data/lib/yard-is-sequel/models.rb +40 -0
- data/lib/yard-is-sequel.rb +23 -0
- metadata +128 -0
data/README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# yard-is-sequel
|
|
2
|
+
|
|
3
|
+
YARD plugin for documenting Sequel ORM models.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`yard-is-sequel` is a YARD plugin that automatically documents Sequel ORM models by extracting information about database fields, associations, and model definitions directly from your Ruby code. It generates comprehensive documentation without requiring manual annotations for basic Sequel features.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Automatic Field Documentation**: Extracts database column information from Sequel models
|
|
12
|
+
- **Association Detection**: Documents `many_to_one`, `one_to_many`, and `many_to_many` associations
|
|
13
|
+
- **Sequel Model Tagging**: Automatically identifies and tags classes that inherit from `Sequel::Model`
|
|
14
|
+
- **Migration Support**: Reads database schema from Sequel migrations
|
|
15
|
+
- **Integration with YARD**: Seamlessly integrates with existing YARD documentation workflows
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
### As a Gem
|
|
20
|
+
|
|
21
|
+
Add to your `Gemfile`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'yard-is-sequel', '~> 0.8'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or install directly:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
gem install yard-is-sequel
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### For Development
|
|
34
|
+
|
|
35
|
+
Add to your project's `.gemspec`:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
spec.add_development_dependency 'yard-is-sequel', '~> 0.8'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
Run YARD with the plugin enabled:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
$ yardoc --plugin is-sequel
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Using `.yardopts` File
|
|
52
|
+
|
|
53
|
+
Add the plugin to your `.yardopts` file:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
--plugin is-sequel
|
|
57
|
+
--markup markdown
|
|
58
|
+
--output-dir ./doc
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Environment Configuration
|
|
62
|
+
|
|
63
|
+
For field detection to work, you need to set the path to your Sequel migrations:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
export SEQUEL_MIGRATIONS_DIR=/path/to/your/migrations
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or set it temporarily:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
SEQUEL_MIGRATIONS_DIR=/path/to/migrations yardoc --plugin is-sequel
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## How It Works
|
|
76
|
+
|
|
77
|
+
### Database Fields
|
|
78
|
+
|
|
79
|
+
The plugin processes tables defined via `set_dataset` or `dataset=`. It:
|
|
80
|
+
- Applies migrations to an in-memory SQLite database
|
|
81
|
+
- Extracts column names, types, and nullability constraints
|
|
82
|
+
- Documents each field as an attribute with proper type annotations
|
|
83
|
+
|
|
84
|
+
Example model:
|
|
85
|
+
```ruby
|
|
86
|
+
class User < Sequel::Model
|
|
87
|
+
set_dataset :users
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Generated documentation will include all fields from the `users` table.
|
|
92
|
+
|
|
93
|
+
### Associations
|
|
94
|
+
|
|
95
|
+
The plugin detects and documents Sequel associations:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
class User < Sequel::Model
|
|
99
|
+
many_to_one :organization
|
|
100
|
+
one_to_many :posts
|
|
101
|
+
many_to_many :roles
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Each association is documented as an attribute with:
|
|
106
|
+
- Appropriate return types
|
|
107
|
+
- Association type tags
|
|
108
|
+
- Grouping under "Sequel Associations"
|
|
109
|
+
|
|
110
|
+
### Model Detection
|
|
111
|
+
|
|
112
|
+
Classes inheriting from `Sequel::Model` are automatically tagged as Sequel models.
|
|
113
|
+
|
|
114
|
+
## Configuration
|
|
115
|
+
|
|
116
|
+
### Migration Directory
|
|
117
|
+
|
|
118
|
+
Set the environment variable for migrations:
|
|
119
|
+
```bash
|
|
120
|
+
export SEQUEL_MIGRATIONS_DIR=./db/migrations
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Plugin Options
|
|
124
|
+
|
|
125
|
+
Currently, the plugin supports the following implicit configurations:
|
|
126
|
+
- Database fields are extracted only when `SEQUEL_MIGRATIONS_DIR` is set
|
|
127
|
+
- All Sequel models in processed files are documented
|
|
128
|
+
- Associations are automatically detected from method calls
|
|
129
|
+
|
|
130
|
+
## Examples
|
|
131
|
+
|
|
132
|
+
### Full Example Model
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# app/models/user.rb
|
|
136
|
+
class User < Sequel::Model(:users)
|
|
137
|
+
# These will be documented automatically
|
|
138
|
+
many_to_one :organization
|
|
139
|
+
one_to_many :posts
|
|
140
|
+
many_to_many :permissions
|
|
141
|
+
|
|
142
|
+
# Custom methods are preserved
|
|
143
|
+
def full_name
|
|
144
|
+
"#{first_name} #{last_name}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Generated Documentation
|
|
150
|
+
|
|
151
|
+
The plugin generates documentation that includes:
|
|
152
|
+
|
|
153
|
+
1. **Fields Section**: Database columns with types
|
|
154
|
+
2. **Associations Section**: All Sequel associations
|
|
155
|
+
3. **Methods Section**: Custom methods (preserved from existing YARD docs)
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
### Setting Up Development Environment
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
git clone https://github.com/inat-get/yard-is-sequel.git
|
|
163
|
+
cd yard-is-sequel
|
|
164
|
+
bundle install
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Running Tests
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
bundle exec rake spec
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Project Structure
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
lib/
|
|
177
|
+
├── yard-is-sequel.rb # Main plugin file
|
|
178
|
+
├── yard-is-sequel/
|
|
179
|
+
│ ├── info.rb # Plugin metadata
|
|
180
|
+
│ ├── models.rb # Model detection handler
|
|
181
|
+
│ ├── associations.rb # Association handler
|
|
182
|
+
│ └── fields.rb # Field extraction handler
|
|
183
|
+
spec/ # Test files
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Contributing
|
|
187
|
+
|
|
188
|
+
1. Fork the repository
|
|
189
|
+
2. Create a feature branch
|
|
190
|
+
3. Add tests for your changes
|
|
191
|
+
4. Ensure all tests pass
|
|
192
|
+
5. Submit a pull request
|
|
193
|
+
|
|
194
|
+
## Requirements
|
|
195
|
+
|
|
196
|
+
- Ruby >= 3.4
|
|
197
|
+
- YARD >= 0.9
|
|
198
|
+
- Sequel >= 5.100
|
|
199
|
+
- SQLite3 >= 2.9 (for migration processing)
|
|
200
|
+
|
|
201
|
+
## Limitations
|
|
202
|
+
|
|
203
|
+
- Field detection requires Sequel migrations
|
|
204
|
+
- Currently supports SQLite for schema extraction (but works with any DB via Sequel)
|
|
205
|
+
- Complex association options might not be fully parsed
|
|
206
|
+
- Database views are not currently supported
|
|
207
|
+
|
|
208
|
+
## Troubleshooting
|
|
209
|
+
|
|
210
|
+
### Fields Not Appearing
|
|
211
|
+
- Ensure `SEQUEL_MIGRATIONS_DIR` is set correctly
|
|
212
|
+
- Verify migrations can be applied without errors
|
|
213
|
+
- Check that your models use `set_dataset` or `dataset=`
|
|
214
|
+
|
|
215
|
+
### Associations Not Documented
|
|
216
|
+
- Verify association method calls are at the class level
|
|
217
|
+
- Check for syntax errors in association definitions
|
|
218
|
+
- Ensure the plugin is loaded (check YARD output)
|
|
219
|
+
|
|
220
|
+
### Plugin Not Loading
|
|
221
|
+
- Verify YARD version compatibility
|
|
222
|
+
- Check `.yardopts` file for correct plugin name
|
|
223
|
+
- Try running with `--debug` flag for more information
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
This project is licensed under the GPL-3.0-or-later License - see the [LICENSE](LICENSE) file for details.
|
|
228
|
+
|
|
229
|
+
## Author
|
|
230
|
+
|
|
231
|
+
Ivan Shikhalev
|
|
232
|
+
|
|
233
|
+
## Repository
|
|
234
|
+
|
|
235
|
+
https://github.com/inat-get/yard-is-sequel
|
|
236
|
+
|
|
237
|
+
## Acknowledgments
|
|
238
|
+
|
|
239
|
+
- YARD team for the excellent documentation tool
|
|
240
|
+
- Sequel ORM maintainers
|
|
241
|
+
- Contributors and users of the plugin
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
*Note: This plugin is designed to complement existing YARD documentation. It automatically adds Sequel-specific documentation but doesn't interfere with manually written documentation.*
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "info"
|
|
4
|
+
|
|
5
|
+
class IS::YARD::Sequel::AssociationsHandler < YARD::Handlers::Ruby::Base
|
|
6
|
+
handles method_call(:many_to_one)
|
|
7
|
+
handles method_call(:one_to_many)
|
|
8
|
+
handles method_call(:many_to_many)
|
|
9
|
+
namespace_only
|
|
10
|
+
|
|
11
|
+
def process
|
|
12
|
+
# Получаем имя ассоциации
|
|
13
|
+
first_param = statement.parameters.first
|
|
14
|
+
assoc_name = first_param.jump(:ident, :string_content).source.to_sym
|
|
15
|
+
|
|
16
|
+
# Определяем тип ассоциации
|
|
17
|
+
assoc_type = statement.method_name.source.to_sym
|
|
18
|
+
|
|
19
|
+
# Извлекаем опции
|
|
20
|
+
options = extract_options
|
|
21
|
+
|
|
22
|
+
# Определяем класс ассоциации
|
|
23
|
+
assoc_class = determine_association_class(assoc_name, options)
|
|
24
|
+
|
|
25
|
+
# Создаем атрибут в документации
|
|
26
|
+
register_attribute(assoc_name, assoc_type, assoc_class)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def extract_options
|
|
32
|
+
options = {}
|
|
33
|
+
|
|
34
|
+
# Ищем хэш опций в параметрах
|
|
35
|
+
statement.parameters.each do |param|
|
|
36
|
+
next unless param && param.respond_to?(:type)
|
|
37
|
+
|
|
38
|
+
if param.type == :hash || param.type == :assoclist || param.type == :list
|
|
39
|
+
process_hash_node(param, options)
|
|
40
|
+
elsif param.type == :bare_assoc_hash
|
|
41
|
+
process_bare_assoc_hash(param, options)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
options
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def process_hash_node(hash_node, options)
|
|
49
|
+
if hash_node.type == :hash
|
|
50
|
+
hash_node.children.each do |child|
|
|
51
|
+
next unless child.respond_to?(:type) && child.type == :assoc
|
|
52
|
+
|
|
53
|
+
key_node, value_node = child.children
|
|
54
|
+
process_assoc_pair(key_node, value_node, options) if key_node && value_node
|
|
55
|
+
end
|
|
56
|
+
elsif hash_node.type == :assoclist || hash_node.type == :list
|
|
57
|
+
hash_node.children.each do |child|
|
|
58
|
+
if child.respond_to?(:type) && child.type == :assoc && child.children.size >= 2
|
|
59
|
+
process_assoc_pair(child.children[0], child.children[1], options)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def process_bare_assoc_hash(hash_node, options)
|
|
66
|
+
hash_node.children.each do |child|
|
|
67
|
+
if child.respond_to?(:type) && child.type == :assoc && child.children.size >= 2
|
|
68
|
+
key_node, value_node = child.children
|
|
69
|
+
process_assoc_pair(key_node, value_node, options)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def process_assoc_pair(key_node, value_node, options)
|
|
75
|
+
return unless key_node && value_node
|
|
76
|
+
|
|
77
|
+
key_name = extract_key_name(key_node)
|
|
78
|
+
return unless key_name
|
|
79
|
+
|
|
80
|
+
options[key_name] = parse_option_value(value_node)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def extract_key_name(key_node)
|
|
84
|
+
return unless key_node.respond_to?(:source)
|
|
85
|
+
|
|
86
|
+
key_source = key_node.source
|
|
87
|
+
key_source = key_source.to_s
|
|
88
|
+
.gsub(/^:/, "")
|
|
89
|
+
.gsub(/:$/, "")
|
|
90
|
+
.gsub(/^["']|["']$/, "")
|
|
91
|
+
|
|
92
|
+
key_source.to_sym
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def parse_option_value(value_node)
|
|
96
|
+
return nil unless value_node && value_node.respond_to?(:source)
|
|
97
|
+
|
|
98
|
+
source = value_node.source
|
|
99
|
+
|
|
100
|
+
case value_node.type
|
|
101
|
+
when :symbol_literal, :symbol
|
|
102
|
+
source = source.to_s.sub(/^:/, "")
|
|
103
|
+
when :string_literal
|
|
104
|
+
source = source.gsub(/^['"]|['"]$/, "")
|
|
105
|
+
when :dyna_symbol
|
|
106
|
+
source = source.to_s.sub(/^:/, "").gsub(/^["']|["']$/, "")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
source
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def determine_association_class(assoc_name, options)
|
|
113
|
+
class_name = options[:class] || options[:class_name]
|
|
114
|
+
|
|
115
|
+
if class_name
|
|
116
|
+
class_name = class_name.to_s
|
|
117
|
+
class_name = class_name.gsub(/^['"]|['"]$/, "")
|
|
118
|
+
return class_name
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class_name = assoc_name.to_s
|
|
122
|
+
.sub(/s$/, "")
|
|
123
|
+
.split("_")
|
|
124
|
+
.map(&:capitalize)
|
|
125
|
+
.join
|
|
126
|
+
|
|
127
|
+
class_name
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def register_attribute(name, assoc_type, assoc_class)
|
|
131
|
+
# Определяем тип атрибута: rw для many_to_one (read/write), r для остальных (read only)
|
|
132
|
+
# attr_type = (assoc_type == :many_to_one) ? "rw" : "r"
|
|
133
|
+
|
|
134
|
+
# Определяем возвращаемый тип
|
|
135
|
+
return_type = case assoc_type
|
|
136
|
+
when :many_to_one
|
|
137
|
+
"#{assoc_class}, nil"
|
|
138
|
+
when :one_to_many, :many_to_many
|
|
139
|
+
"Sequel::Dataset, Array<#{assoc_class}>"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Создаем объект метода с директивой @!attribute
|
|
143
|
+
method_obj = YARD::CodeObjects::MethodObject.new(
|
|
144
|
+
namespace,
|
|
145
|
+
name,
|
|
146
|
+
:instance
|
|
147
|
+
)
|
|
148
|
+
method_obj.is_attribute = true
|
|
149
|
+
method_obj.group = 'Sequel Associations'
|
|
150
|
+
namespace.attributes[:instance][name] = {
|
|
151
|
+
read: method_obj,
|
|
152
|
+
write: method_obj
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Формируем документацию с директивой @!attribute
|
|
156
|
+
docstring_parts = []
|
|
157
|
+
|
|
158
|
+
# Директива @!attribute должна быть первой
|
|
159
|
+
# docstring_parts << "@!attribute [#{attr_type}]"
|
|
160
|
+
docstring_parts << "@return [#{return_type}]"
|
|
161
|
+
|
|
162
|
+
# Описание
|
|
163
|
+
docstring_parts << "Sequel #{assoc_type.to_s.gsub('_', '-')} association."
|
|
164
|
+
|
|
165
|
+
# Примеры
|
|
166
|
+
# example_content = generate_example(name, assoc_type, assoc_class)
|
|
167
|
+
# if example_content && !example_content.empty?
|
|
168
|
+
# docstring_parts << ""
|
|
169
|
+
# docstring_parts << "@example"
|
|
170
|
+
# example_content.lines.each do |line|
|
|
171
|
+
# docstring_parts << " #{line.chomp}"
|
|
172
|
+
# end
|
|
173
|
+
# end
|
|
174
|
+
|
|
175
|
+
method_obj.docstring = docstring_parts.join("\n")
|
|
176
|
+
|
|
177
|
+
# Добавляем тег для фильтрации
|
|
178
|
+
method_obj.add_tag(YARD::Tags::Tag.new(:sequel_association, assoc_type.to_s))
|
|
179
|
+
|
|
180
|
+
# Регистрируем в YARD
|
|
181
|
+
register(method_obj)
|
|
182
|
+
|
|
183
|
+
log.debug "[Sequel Plugin] Added association attribute: #{namespace}##{name} (#{assoc_type})"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def generate_example(name, assoc_type, assoc_class)
|
|
187
|
+
namespace_name = namespace.name.to_s
|
|
188
|
+
model_name = namespace_name.split("::").last.downcase
|
|
189
|
+
|
|
190
|
+
case assoc_type
|
|
191
|
+
when :many_to_one
|
|
192
|
+
<<~EXAMPLE.chomp
|
|
193
|
+
#{model_name}.#{name} # => #{assoc_class} instance or nil
|
|
194
|
+
#{model_name}.#{name} = #{assoc_class}.first
|
|
195
|
+
EXAMPLE
|
|
196
|
+
when :one_to_many
|
|
197
|
+
singular_name = name.to_s.chomp("s")
|
|
198
|
+
<<~EXAMPLE.chomp
|
|
199
|
+
#{model_name}.#{name} # => Dataset of #{assoc_class} objects
|
|
200
|
+
#{model_name}.add_#{singular_name}(#{assoc_class}.new)
|
|
201
|
+
EXAMPLE
|
|
202
|
+
when :many_to_many
|
|
203
|
+
singular_name = name.to_s.chomp("s")
|
|
204
|
+
<<~EXAMPLE.chomp
|
|
205
|
+
#{model_name}.#{name} # => Dataset of #{assoc_class} objects
|
|
206
|
+
#{model_name}.add_#{singular_name}(#{assoc_class}.first)
|
|
207
|
+
#{model_name}.#{name}_dataset # Many-to-many through join table
|
|
208
|
+
EXAMPLE
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sequel'
|
|
4
|
+
|
|
5
|
+
class IS::YARD::Sequel::FieldsHandler < YARD::Handlers::Ruby::Base
|
|
6
|
+
|
|
7
|
+
handles method_call(:set_dataset)
|
|
8
|
+
handles method_call(:dataset=)
|
|
9
|
+
|
|
10
|
+
def process
|
|
11
|
+
table_name = statement.parameters.first.jump(:symbol, :string).source.delete(':"\'')
|
|
12
|
+
database = self.class.database
|
|
13
|
+
return unless database
|
|
14
|
+
table_schema = database.schema(table_name)
|
|
15
|
+
return unless table_schema
|
|
16
|
+
|
|
17
|
+
table_schema.each do |column_name, info|
|
|
18
|
+
type = [ map_type(info[:db_type]) ]
|
|
19
|
+
type << 'nil' unless info[:allow_null] == false
|
|
20
|
+
|
|
21
|
+
object = YARD::CodeObjects::MethodObject.new(namespace, column_name) do |o|
|
|
22
|
+
o.source = statement.source
|
|
23
|
+
o.parameters = []
|
|
24
|
+
o.docstring = 'Sequel data field'
|
|
25
|
+
o.docstring.add_tag YARD::Tags::Tag::new(:return, '', type)
|
|
26
|
+
o.docstring.add_tag YARD::Tags::Tag::new(:sequel_field, '')
|
|
27
|
+
o.is_attribute = true
|
|
28
|
+
o.group = 'Sequel Fields'
|
|
29
|
+
end
|
|
30
|
+
register object
|
|
31
|
+
namespace.attributes[:instance][column_name] = {
|
|
32
|
+
read: object,
|
|
33
|
+
write: object
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def map_type source
|
|
41
|
+
case source.to_s.downcase
|
|
42
|
+
when 'integer'
|
|
43
|
+
'Integer'
|
|
44
|
+
when 'float', 'double precision'
|
|
45
|
+
'Float'
|
|
46
|
+
when /^char/, /^varchar/, 'text'
|
|
47
|
+
'String'
|
|
48
|
+
when 'timestamp', 'datetime', 'time'
|
|
49
|
+
'Time'
|
|
50
|
+
when 'boolean'
|
|
51
|
+
'Boolean'
|
|
52
|
+
else
|
|
53
|
+
source
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class << self
|
|
58
|
+
|
|
59
|
+
def database
|
|
60
|
+
@database ||= setup_database
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def setup_database
|
|
66
|
+
# return unless defined?($SEQUEL_MIGRATIONS_DIR) && $SEQUEL_MIGRATIONS_DIR
|
|
67
|
+
begin
|
|
68
|
+
Sequel.extension :migration
|
|
69
|
+
db = Sequel.sqlite
|
|
70
|
+
Sequel::Migrator::run db, ENV['SEQUEL_MIGRATIONS_DIR']
|
|
71
|
+
db
|
|
72
|
+
rescue
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module IS; end
|
|
4
|
+
module IS::YARD; end
|
|
5
|
+
module IS::YARD::Sequel; end
|
|
6
|
+
|
|
7
|
+
module IS::YARD::Sequel::Info
|
|
8
|
+
|
|
9
|
+
NAME = 'yard-is-sequel'
|
|
10
|
+
VERSION = '0.8.0'
|
|
11
|
+
SUMMARY = 'YARD-plugin for Sequel-models documenting'
|
|
12
|
+
AUTHOR = 'Ivan Shikhalev'
|
|
13
|
+
LICENSE = 'GPL-3.0-or-later'
|
|
14
|
+
HOMEPAGE = 'https://github.com/inat-get/yard-is-sequel'
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# require "yard"
|
|
4
|
+
|
|
5
|
+
require_relative "info"
|
|
6
|
+
|
|
7
|
+
class IS::YARD::Sequel::ModelHandler < YARD::Handlers::Ruby::ClassHandler
|
|
8
|
+
handles :class
|
|
9
|
+
|
|
10
|
+
def process
|
|
11
|
+
# Проверяем, наследуется ли класс от Sequel::Model
|
|
12
|
+
return unless sequel_model?
|
|
13
|
+
|
|
14
|
+
# Добавляем тег к классу, что это Sequel модель
|
|
15
|
+
# Используем пустое значение для тега
|
|
16
|
+
namespace.add_tag(YARD::Tags::Tag.new(:sequel_model, ""))
|
|
17
|
+
|
|
18
|
+
log.debug "[Sequel Plugin] Detected Sequel model: #{namespace}"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def sequel_model?
|
|
24
|
+
# Проверяем наличие суперкласса
|
|
25
|
+
superclass = statement.superclass
|
|
26
|
+
return false unless superclass
|
|
27
|
+
|
|
28
|
+
# Получаем исходный код суперкласса как строку
|
|
29
|
+
superclass_source = superclass.source
|
|
30
|
+
|
|
31
|
+
# Проверяем разные варианты записи Sequel::Model:
|
|
32
|
+
# 1. class User < Sequel::Model
|
|
33
|
+
# 2. class User < Sequel::Model(:users)
|
|
34
|
+
# 3. class User < Sequel::Model(DB[:users])
|
|
35
|
+
|
|
36
|
+
# Простая проверка по подстроке
|
|
37
|
+
superclass_source =~ /Sequel::Model/
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "yard-is-sequel/info"
|
|
4
|
+
|
|
5
|
+
module IS::YARD::Sequel
|
|
6
|
+
def self.init
|
|
7
|
+
register_tags
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.register_tags
|
|
11
|
+
YARD::Tags::Library.define_tag 'Sequel Model', :sequel_model
|
|
12
|
+
YARD::Tags::Library.define_tag "Sequel Field", :sequel_field
|
|
13
|
+
YARD::Tags::Library.define_tag 'Sequel Association', :sequel_association
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require_relative 'yard-is-sequel/associations'
|
|
18
|
+
require_relative 'yard-is-sequel/models'
|
|
19
|
+
require_relative 'yard-is-sequel/fields'
|
|
20
|
+
|
|
21
|
+
YARD::Handlers::Processor.register_handler_namespace(:sequel, IS::YARD::Sequel)
|
|
22
|
+
|
|
23
|
+
IS::YARD::Sequel.init
|