ficha 0.1.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/CHANGELOG.md +42 -0
- data/README.md +204 -0
- data/bin/console +11 -0
- data/bin/ficha +9 -0
- data/bin/setup +8 -0
- data/exe/ficha +9 -0
- data/ficha.gemspec +42 -0
- data/lib/ficha/config.rb +36 -0
- data/lib/ficha/engine.rb +93 -0
- data/lib/ficha/template.rb +34 -0
- data/lib/ficha/version.rb +5 -0
- data/lib/ficha.rb +98 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3ac8590b6ab46f565f13b1bb959e55c790f8ecebb62a7946b47be7e3acd94a88
|
|
4
|
+
data.tar.gz: 6290916d3e12018298de9343a6d4803ce2b7beb5d983c19a9f26c1b32993426f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 68dbe337310fa333176a6b7341d479dec441af04de24dc1e811307631432c3d459b4aaf31fbbf5272148e2b9cef6971aeb9f43e6703f129b2c9314d66236a4d7
|
|
7
|
+
data.tar.gz: b5216fd9da5ed299891069e10101af2598a3a68c36cdd31ef2b02b7d59e42be5a1d32dd596d8aaf963418c57e752718d44fa5167371362aa15b8c9f7a1c8fe07
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `ficha` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Refactored `Ficha::CLI#run` into smaller private methods to improve readability and comply with RuboCop metrics (ABC size, method length).
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Auto-corrected code style with RuboCop.
|
|
17
|
+
|
|
18
|
+
### Internal
|
|
19
|
+
|
|
20
|
+
- Added RuboCop linting to GitHub Actions CI.
|
|
21
|
+
|
|
22
|
+
## [0.1.0] - 2025-12-09
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Initial CLI with `--help` flag and basic argument parsing.
|
|
27
|
+
- Core `Engine` class for resolving and filtering project files based on strategies.
|
|
28
|
+
- Support for `--dry-run` mode with end-to-end tests.
|
|
29
|
+
- Config file loading mechanism.
|
|
30
|
+
- Basic templating capabilities (foundation for future expansion).
|
|
31
|
+
- End-to-end test suite with fixtures.
|
|
32
|
+
|
|
33
|
+
### Internal
|
|
34
|
+
|
|
35
|
+
- Improved test fixtures and added e2e test scaffolding.
|
|
36
|
+
- Finalized `ficha.gemspec` for public release under AGPL-3.0.
|
|
37
|
+
- Added LICENSE file (AGPL-3.0).
|
|
38
|
+
- Initial `.gitignore` and gem scaffolding.
|
|
39
|
+
|
|
40
|
+
### Changelog
|
|
41
|
+
|
|
42
|
+
- This changelog was introduced retroactively to document project evolution from the first commit.
|
data/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# ficha
|
|
2
|
+
|
|
3
|
+
> **Feature-aware file dumper for Ruby projects**
|
|
4
|
+
> Extract only the files you need — by model, endpoint, feature, or safe subset — without manual grepping.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
`ficha` helps you **dump relevant files** from large Ruby/Rails codebases based on **semantic strategies**, not just file extensions.
|
|
10
|
+
Think of it as `grep` + `find` + domain knowledge — for developers who know *what* they need, but not *where* it is.
|
|
11
|
+
|
|
12
|
+
Perfect for:
|
|
13
|
+
|
|
14
|
+
- Preparing minimal bug reproductions
|
|
15
|
+
- Sharing feature context with teammates
|
|
16
|
+
- Migrating subsystems
|
|
17
|
+
- Auditing legacy code
|
|
18
|
+
- Generating focused backups
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🔍 Quick Example
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
# Dump all files related to the `User` model
|
|
26
|
+
ficha by-model:User
|
|
27
|
+
|
|
28
|
+
# Dump a "safe" subset (models, serializers, controllers, specs)
|
|
29
|
+
ficha full-safe
|
|
30
|
+
|
|
31
|
+
# Preview without writing
|
|
32
|
+
ficha by-model:Order --dry-run
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Output (dry-run):
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
app/models/user.rb
|
|
39
|
+
app/controllers/users_controller.rb
|
|
40
|
+
app/serializers/user_serializer.rb
|
|
41
|
+
spec/models/user_spec.rb
|
|
42
|
+
...
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🧩 Core Concepts
|
|
48
|
+
|
|
49
|
+
`ficha` works by combining:
|
|
50
|
+
|
|
51
|
+
1. **Strategies** — rules that define *what to include* (e.g. `by-model`, `full-safe`)
|
|
52
|
+
2. **Config** — your project-specific paths, conventions, and overrides (`ficha.yml`)
|
|
53
|
+
3. **Engine** — the resolver that applies strategies to your codebase
|
|
54
|
+
4. **CLI** — your interface to run queries
|
|
55
|
+
|
|
56
|
+
```mermaid
|
|
57
|
+
graph LR
|
|
58
|
+
A[CLI: ficha by-model:User] --> B(Engine)
|
|
59
|
+
B --> C{Strategy: by-model}
|
|
60
|
+
B --> D[Config: ficha.yml]
|
|
61
|
+
C --> E[Match model, controller, serializer...]
|
|
62
|
+
D --> F[Custom paths, excludes]
|
|
63
|
+
E --> G[File list]
|
|
64
|
+
F --> G
|
|
65
|
+
G --> H[Output or copy]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 🚀 Installation
|
|
71
|
+
|
|
72
|
+
Add to your project’s `Gemfile`:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
group :development do
|
|
76
|
+
gem "ficha", require: false
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then run:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
bundle install
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> 💡 `ficha` is designed as a **dev-only CLI tool** — it doesn’t affect your runtime.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## ⚙️ Configuration
|
|
91
|
+
|
|
92
|
+
By default, `ficha` looks for `config/ficha.yml` (in Rails) or `.ficha.yml` (in any project).
|
|
93
|
+
|
|
94
|
+
### Example `config/ficha.yml`
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
# ficha.yml — declarative, extensible, no Ruby code!
|
|
98
|
+
|
|
99
|
+
strategies:
|
|
100
|
+
by-model:
|
|
101
|
+
include:
|
|
102
|
+
- "app/models/{{name}}.rb"
|
|
103
|
+
- "app/controllers/{{name.pluralize}}_controller.rb"
|
|
104
|
+
- "app/serializers/{{name}}_serializer.rb"
|
|
105
|
+
- "spec/models/{{name}}_spec.rb"
|
|
106
|
+
- "spec/requests/{{name.pluralize}}_spec.rb"
|
|
107
|
+
exclude:
|
|
108
|
+
- "app/models/concerns/*"
|
|
109
|
+
|
|
110
|
+
full-safe:
|
|
111
|
+
include:
|
|
112
|
+
- "app/models/**/*.rb"
|
|
113
|
+
- "app/controllers/**/*.rb"
|
|
114
|
+
- "app/serializers/**/*.rb"
|
|
115
|
+
- "spec/{models,controllers,serializers}/**/*.rb"
|
|
116
|
+
exclude:
|
|
117
|
+
- "spec/support/**/*"
|
|
118
|
+
|
|
119
|
+
paths:
|
|
120
|
+
root: "." # project root
|
|
121
|
+
output: "/tmp/ficha-out" # (future: where to copy files)
|
|
122
|
+
|
|
123
|
+
excludes:
|
|
124
|
+
- "tmp/**/*"
|
|
125
|
+
- "log/**/*"
|
|
126
|
+
- "vendor/**/*"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
> ✅ **Philosophy**: Keep logic in config, not code.
|
|
130
|
+
> 🔒 Config supports ERB if you *really* need dynamic values (but avoid it for reproducibility).
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🎯 Built-in Strategies
|
|
135
|
+
|
|
136
|
+
| Strategy | Purpose |
|
|
137
|
+
|---------------|--------|
|
|
138
|
+
| `full-safe` | Dumps core app code: models, controllers, serializers, related specs |
|
|
139
|
+
| `by-model:X` | Dumps everything related to model `X` (convention-based) |
|
|
140
|
+
| *(more coming)* | — |
|
|
141
|
+
|
|
142
|
+
You can **extend strategies** in your `ficha.yml` — no need to fork the gem.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 🛠 CLI Usage
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
ficha [STRATEGY:PARAMS...] [options]
|
|
150
|
+
|
|
151
|
+
Examples:
|
|
152
|
+
ficha # runs 'full-safe'
|
|
153
|
+
ficha by-model:User # model-aware dump
|
|
154
|
+
ficha full-safe --dry-run # preview only
|
|
155
|
+
|
|
156
|
+
Options:
|
|
157
|
+
--help Show help
|
|
158
|
+
--dry-run Print matched files (default)
|
|
159
|
+
--exclude GLOB Exclude additional paths (can be used multiple times)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
> ⚠️ **Non-dry-run mode** (actual file copying) is **not implemented yet** — coming soon!
|
|
163
|
+
> You’ll be able to dump to a directory, zip, or tar.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 🧪 Testing & Development
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
bundle exec rake # runs tests + RuboCop
|
|
171
|
+
bundle exec bin/ficha # run from source
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Tests use real fixtures in `test/fixtures/rails_app/` — easy to extend.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 📜 License
|
|
179
|
+
|
|
180
|
+
AGPL-3.0 — because **privacy, self-hosting, and user freedom matter**.
|
|
181
|
+
|
|
182
|
+
You’re free to use `ficha` in any project, but if you modify and redistribute it (e.g. as a hosted service), you must share the source.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 🙌 Contributing
|
|
187
|
+
|
|
188
|
+
PRs welcome! Especially:
|
|
189
|
+
|
|
190
|
+
- New strategies
|
|
191
|
+
- Non-dry-run output formats (dir, zip, tar)
|
|
192
|
+
- Better Rails/7+ support
|
|
193
|
+
- Performance optimizations
|
|
194
|
+
|
|
195
|
+
Please:
|
|
196
|
+
|
|
197
|
+
- Write tests
|
|
198
|
+
- Keep config declarative
|
|
199
|
+
- Respect the AGPL spirit
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
> Made with 💎 by a Ruby dev who hates manual file hunting.
|
|
204
|
+
> Inspired by `rails:template`, `bundle gem`, and the pain of legacy monoliths.
|
data/bin/console
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "ficha"
|
|
6
|
+
|
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
+
|
|
10
|
+
require "irb"
|
|
11
|
+
IRB.start(__FILE__)
|
data/bin/ficha
ADDED
data/bin/setup
ADDED
data/exe/ficha
ADDED
data/ficha.gemspec
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/ficha/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "ficha"
|
|
7
|
+
spec.version = Ficha::VERSION
|
|
8
|
+
spec.authors = ["Ilmir Karimov"]
|
|
9
|
+
spec.email = ["code.for.func@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Feature-aware file dumper for Ruby projects"
|
|
12
|
+
spec.description = <<~DESCRIPTION
|
|
13
|
+
Extract only the files you need — by model, endpoint, feature, or safe subset —
|
|
14
|
+
without manual grepping.
|
|
15
|
+
DESCRIPTION
|
|
16
|
+
spec.homepage = "https://github.com/ilmir/ficha"
|
|
17
|
+
spec.license = "AGPL-3.0-or-later"
|
|
18
|
+
spec.required_ruby_version = ">= 3.0"
|
|
19
|
+
|
|
20
|
+
spec.files = Dir[
|
|
21
|
+
"lib/**/*",
|
|
22
|
+
"bin/*",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE.txt",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"ficha.gemspec"
|
|
27
|
+
].reject { |f| File.directory?(f) }
|
|
28
|
+
|
|
29
|
+
spec.bindir = "exe"
|
|
30
|
+
spec.executables = ["ficha"]
|
|
31
|
+
spec.require_paths = ["lib"]
|
|
32
|
+
|
|
33
|
+
spec.add_dependency "activesupport", ">= 6.0", "< 9.0"
|
|
34
|
+
|
|
35
|
+
spec.metadata = {
|
|
36
|
+
"homepage_uri" => spec.homepage,
|
|
37
|
+
"source_code_uri" => "https://github.com/ilmir/ficha",
|
|
38
|
+
"changelog_uri" => "https://github.com/ilmir/ficha/blob/main/CHANGELOG.md",
|
|
39
|
+
"bug_tracker_uri" => "https://github.com/ilmir/ficha/issues",
|
|
40
|
+
"rubygems_mfa_required" => "true"
|
|
41
|
+
}
|
|
42
|
+
end
|
data/lib/ficha/config.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module Ficha
|
|
6
|
+
class Config
|
|
7
|
+
DEFAULT_CONFIG_PATH = File.expand_path("../../data/base.ficha.yml", __dir__)
|
|
8
|
+
|
|
9
|
+
def self.load(config_path = nil)
|
|
10
|
+
config_path ||= find_config
|
|
11
|
+
if config_path && File.exist?(config_path)
|
|
12
|
+
raw = YAML.safe_load_file(config_path, aliases: true)
|
|
13
|
+
raw ||= {}
|
|
14
|
+
else
|
|
15
|
+
raw = YAML.safe_load_file(DEFAULT_CONFIG_PATH, aliases: true)
|
|
16
|
+
end
|
|
17
|
+
new(raw)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(data)
|
|
21
|
+
@data = data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def strategies
|
|
25
|
+
@data.fetch("strategies", {})
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def defaults
|
|
29
|
+
@data.fetch("defaults", {})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.find_config
|
|
33
|
+
["./.ficha.yml", "./ficha.yml"].find { |f| File.exist?(f) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/ficha/engine.rb
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require_relative "template"
|
|
5
|
+
require_relative "config"
|
|
6
|
+
|
|
7
|
+
module Ficha
|
|
8
|
+
class Engine
|
|
9
|
+
SENSITIVE_PATTERNS = [
|
|
10
|
+
/\.env/,
|
|
11
|
+
/master\.key/,
|
|
12
|
+
/credentials\.yml\.enc/
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
def initialize(config:, queries:, overrides: {})
|
|
16
|
+
@config = config
|
|
17
|
+
@queries = queries
|
|
18
|
+
@overrides = overrides
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def run
|
|
22
|
+
files = @queries.flat_map { |q| resolve_query(q) }
|
|
23
|
+
files = apply_filters(files)
|
|
24
|
+
files.uniq.sort
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def resolve_query(query_str)
|
|
30
|
+
parsed = parse_query(query_str)
|
|
31
|
+
strategy_name = parsed[:name]
|
|
32
|
+
params = parsed[:params]
|
|
33
|
+
|
|
34
|
+
strategy = @config.strategies[strategy_name]
|
|
35
|
+
raise "strategy '#{strategy_name}' not found in .ficha.yml" unless strategy
|
|
36
|
+
|
|
37
|
+
param_hash = build_param_hash(strategy["params"] || [], params)
|
|
38
|
+
|
|
39
|
+
# Раскрываем include_paths через шаблоны и glob
|
|
40
|
+
strategy["include_paths"].flat_map do |pattern|
|
|
41
|
+
rendered = Template.render(pattern, param_hash)
|
|
42
|
+
Dir.glob(rendered)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse_query(query_str)
|
|
47
|
+
if query_str.include?(":")
|
|
48
|
+
parts = query_str.split(":", 2)
|
|
49
|
+
name = parts[0]
|
|
50
|
+
param_str = parts[1]
|
|
51
|
+
params = param_str.include?(",") ? param_str.split(",") : [param_str]
|
|
52
|
+
{ name: name, params: params }
|
|
53
|
+
else
|
|
54
|
+
{ name: query_str, params: [] }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build_param_hash(expected_params, given_params)
|
|
59
|
+
expected_params.map(&:to_sym).zip(given_params).to_h
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def apply_filters(files)
|
|
63
|
+
files = filter_by_sensitive_names(files)
|
|
64
|
+
files = filter_by_cli_excludes(files)
|
|
65
|
+
filter_by_ignored_extensions(files)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def filter_by_sensitive_names(files)
|
|
69
|
+
files.reject do |path|
|
|
70
|
+
SENSITIVE_PATTERNS.any? { |pat| path.match?(pat) }
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def filter_by_cli_excludes(files)
|
|
75
|
+
excludes = @overrides[:exclude_paths] || []
|
|
76
|
+
files.reject do |path|
|
|
77
|
+
excludes.any? do |exclude_glob|
|
|
78
|
+
# Если glob не содержит *, **, ? — считаем, что это директория → добавляем /**
|
|
79
|
+
glob = exclude_glob.include?("*") || exclude_glob.include?("?") ? exclude_glob : "#{exclude_glob}/**/*"
|
|
80
|
+
File.fnmatch(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def filter_by_ignored_extensions(files)
|
|
86
|
+
ignored_exts = (@config.defaults["ignored_extensions"] || []).map(&:downcase)
|
|
87
|
+
files.reject do |path|
|
|
88
|
+
ext = File.extname(path).downcase
|
|
89
|
+
ignored_exts.include?(ext)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/inflector"
|
|
4
|
+
|
|
5
|
+
module Ficha
|
|
6
|
+
module Template
|
|
7
|
+
FILTERS = {
|
|
8
|
+
"underscore" => lambda(&:underscore),
|
|
9
|
+
"pluralize" => lambda(&:pluralize),
|
|
10
|
+
"singularize" => lambda(&:singularize),
|
|
11
|
+
"camelize" => lambda(&:camelize)
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def self.render(template_str, bindings = {})
|
|
15
|
+
template_str.gsub(/\{\{\s*(.+?)\s*\}\}/) do
|
|
16
|
+
expr = ::Regexp.last_match(1)
|
|
17
|
+
apply_filters(expr.strip, bindings)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.apply_filters(expr, bindings)
|
|
22
|
+
parts = expr.split("|").map(&:strip)
|
|
23
|
+
var_name = parts.first
|
|
24
|
+
value = bindings[var_name.to_sym] || var_name
|
|
25
|
+
apply_pipeline(value, parts.drop(1))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.apply_pipeline(value, filters)
|
|
29
|
+
filters.reduce(value) do |acc, filter_name|
|
|
30
|
+
FILTERS.fetch(filter_name, ->(x) { x }).call(acc)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/ficha.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ficha/version"
|
|
4
|
+
require_relative "ficha/config"
|
|
5
|
+
require_relative "ficha/template"
|
|
6
|
+
require_relative "ficha/engine"
|
|
7
|
+
require "optparse"
|
|
8
|
+
|
|
9
|
+
module Ficha
|
|
10
|
+
class CLI
|
|
11
|
+
def self.start(argv)
|
|
12
|
+
new(argv).run
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(argv)
|
|
16
|
+
@argv = argv
|
|
17
|
+
@options = { dry_run: false }
|
|
18
|
+
@queries = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def run
|
|
22
|
+
parse!
|
|
23
|
+
|
|
24
|
+
handle_help! if @options[:help]
|
|
25
|
+
ensure_default_query!
|
|
26
|
+
|
|
27
|
+
files = execute_engine
|
|
28
|
+
process_files(files)
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
warn "Error: #{e.message}"
|
|
31
|
+
exit 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def handle_help!
|
|
37
|
+
puts help_text
|
|
38
|
+
exit 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def ensure_default_query!
|
|
42
|
+
@queries << "full-safe" if @queries.empty?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def process_files(files)
|
|
46
|
+
if @options[:dry_run]
|
|
47
|
+
files.each { |f| puts f }
|
|
48
|
+
else
|
|
49
|
+
warn "Non-dry-run mode not implemented yet"
|
|
50
|
+
exit 1
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute_engine
|
|
55
|
+
config = Config.load
|
|
56
|
+
engine = Engine.new(
|
|
57
|
+
config: config,
|
|
58
|
+
queries: @queries,
|
|
59
|
+
overrides: {
|
|
60
|
+
exclude_paths: @options[:exclude_paths],
|
|
61
|
+
dry_run: @options[:dry_run]
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
engine.run
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse!
|
|
68
|
+
@options[:exclude_paths] = []
|
|
69
|
+
|
|
70
|
+
OptionParser.new do |opts|
|
|
71
|
+
opts.banner = "Usage: ficha [STRATEGY:PARAMS...] [options]"
|
|
72
|
+
opts.on("--help", "Show this help") { @options[:help] = true }
|
|
73
|
+
opts.on("--dry-run", "Show files without copying") { @options[:dry_run] = true }
|
|
74
|
+
opts.on("--exclude PATTERN", "Exclude paths matching glob pattern") do |pattern|
|
|
75
|
+
@options[:exclude_paths] << pattern
|
|
76
|
+
end
|
|
77
|
+
end.parse!(@argv)
|
|
78
|
+
|
|
79
|
+
@queries = @argv.dup
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def help_text
|
|
83
|
+
<<~HELP
|
|
84
|
+
ficha — feature-aware file dumper for Ruby projects
|
|
85
|
+
|
|
86
|
+
Usage:
|
|
87
|
+
ficha [STRATEGY:PARAMS...] [options]
|
|
88
|
+
|
|
89
|
+
Examples:
|
|
90
|
+
ficha # full-safe dump
|
|
91
|
+
ficha by-model:User # dump User-related files
|
|
92
|
+
ficha full-safe --dry-run
|
|
93
|
+
|
|
94
|
+
Options:
|
|
95
|
+
HELP
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ficha
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ilmir Karimov
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activesupport
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '6.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '6.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9.0'
|
|
32
|
+
description: |
|
|
33
|
+
Extract only the files you need — by model, endpoint, feature, or safe subset —
|
|
34
|
+
without manual grepping.
|
|
35
|
+
email:
|
|
36
|
+
- code.for.func@gmail.com
|
|
37
|
+
executables:
|
|
38
|
+
- ficha
|
|
39
|
+
extensions: []
|
|
40
|
+
extra_rdoc_files: []
|
|
41
|
+
files:
|
|
42
|
+
- CHANGELOG.md
|
|
43
|
+
- README.md
|
|
44
|
+
- bin/console
|
|
45
|
+
- bin/ficha
|
|
46
|
+
- bin/setup
|
|
47
|
+
- exe/ficha
|
|
48
|
+
- ficha.gemspec
|
|
49
|
+
- lib/ficha.rb
|
|
50
|
+
- lib/ficha/config.rb
|
|
51
|
+
- lib/ficha/engine.rb
|
|
52
|
+
- lib/ficha/template.rb
|
|
53
|
+
- lib/ficha/version.rb
|
|
54
|
+
homepage: https://github.com/ilmir/ficha
|
|
55
|
+
licenses:
|
|
56
|
+
- AGPL-3.0-or-later
|
|
57
|
+
metadata:
|
|
58
|
+
homepage_uri: https://github.com/ilmir/ficha
|
|
59
|
+
source_code_uri: https://github.com/ilmir/ficha
|
|
60
|
+
changelog_uri: https://github.com/ilmir/ficha/blob/main/CHANGELOG.md
|
|
61
|
+
bug_tracker_uri: https://github.com/ilmir/ficha/issues
|
|
62
|
+
rubygems_mfa_required: 'true'
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '3.0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.6.9
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Feature-aware file dumper for Ruby projects
|
|
80
|
+
test_files: []
|