git-fit 0.0.1 → 0.1.1

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: 0d97c84ade46b10cea220c7e65086878e6a7159ae7ba7fa7ecb6fd9e9901852e
4
- data.tar.gz: c8fcdf324bfd39db52710ed9d41cffbfdb9bae4d4e1c060dd9272615efcec054
3
+ metadata.gz: 9551667addc842c5ddb8357d32b9d4eb040eedc236eeb3c940f59ea52f8be329
4
+ data.tar.gz: 3ede4c5df18fd6a3380b5aff8845bf2ac1ca7d1074c2507e647e05363bf745fb
5
5
  SHA512:
6
- metadata.gz: 2f16d136fcf36ed73a0d87b2a24f4c8ef464e9dc20b9f740076e88f8baa7bab0f1fe50b6c4352846488a545dd602e91fc912022a61fa4dfcae94e2d2b40b8aa5
7
- data.tar.gz: 0fff6b9bb61063b69ec46b06aea41296aec5420c5cce1a207a6a95ebef831b1dd2d6fe8b54c745ba401054ee5b4fb1c84118a036339e1316fce2a08a1f3ff640
6
+ metadata.gz: 437969ed9c1dd8ec94de2449cbe8e71e8201bcfe2299fd3dcdcf44c8ae6b4901482b99fdd3459482395896a10c96c455a1acff8c69fd2e242c81e1befdb88941
7
+ data.tar.gz: 11ffe29a5aecb4c305f361c81ad3eef6f73394fc9abb00f75486d68a32caac3de565ef941236120622b63c61059b20486f790e32290ef016da93141266d20b5e
data/lib/git-fit.rb CHANGED
@@ -1,4 +1,22 @@
1
1
  require "thor"
2
+ require "yaml"
3
+ require "fileutils"
4
+
5
+ module GitFit
6
+ @registered_configs = []
7
+
8
+ class << self
9
+ attr_reader :registered_configs
10
+
11
+ def register_config_source(prefix:, keys:, config_path:)
12
+ @registered_configs << { prefix: prefix, keys: keys, config_path: config_path }
13
+ end
14
+ end
15
+ end
2
16
 
3
17
  require_relative "git_fit/version"
18
+ require_relative "git_fit/config"
19
+ require_relative "git_fit/config_template"
20
+ require_relative "git_fit/sync/base"
21
+ require_relative "git_fit/sync/strava"
4
22
  require_relative "git_fit/cli"
data/lib/git_fit/cli.rb CHANGED
@@ -4,6 +4,8 @@ module GitFit
4
4
  class CLI < Thor
5
5
  package_name "git-fit"
6
6
 
7
+ class_option :config, type: :string, desc: "Config file path", aliases: "-c"
8
+
7
9
  def self.exit_on_failure?
8
10
  true
9
11
  end
@@ -12,5 +14,48 @@ module GitFit
12
14
  def version
13
15
  puts "git-fit v#{GitFit::VERSION}"
14
16
  end
17
+
18
+ desc "init", "Generate config.yml"
19
+ option :output, type: :string, desc: "Output path", aliases: "-o"
20
+ def init
21
+ path = options[:output] || "config/config.yml"
22
+ FileUtils.mkdir_p(File.dirname(path))
23
+
24
+ if File.exist?(path)
25
+ existing = YAML.safe_load(File.read(path)) || {}
26
+ merged = deep_merge(ConfigTemplate.hash, existing)
27
+ File.write(path, YAML.dump(merged))
28
+ say_status :merged, path, :green
29
+ else
30
+ File.write(path, ConfigTemplate.generate)
31
+ say_status :created, path, :green
32
+ end
33
+ end
34
+
35
+ no_commands do
36
+ def git_fit_config
37
+ @git_fit_config ||= GitFit::Config.new(options[:config])
38
+ end
39
+
40
+ def deep_merge(template, existing)
41
+ result = deep_dup(template)
42
+ existing.each do |key, val|
43
+ if val.is_a?(Hash) && result[key].is_a?(Hash)
44
+ result[key] = deep_merge(result[key], val)
45
+ else
46
+ result[key] = val
47
+ end
48
+ end
49
+ result
50
+ end
51
+
52
+ def deep_dup(obj)
53
+ case obj
54
+ when Hash then obj.each_with_object({}) { |(k, v), h| h[k] = deep_dup(v) }
55
+ when Array then obj.map { |v| deep_dup(v) }
56
+ else obj
57
+ end
58
+ end
59
+ end
15
60
  end
16
61
  end
@@ -0,0 +1,124 @@
1
+ require "yaml"
2
+
3
+ module GitFit
4
+ # 三优先级: env var > YAML > DEFAULTS
5
+ class Config
6
+ DEFAULTS = {
7
+ "database" => { "path" => "db/fit.sqlite3" },
8
+ "sync" => {},
9
+ "export" => {
10
+ "json" => { "path" => "site/activities.json" },
11
+ "svg" => { "dir" => "site/svg" },
12
+ "csv" => { "path" => "site/workouts.csv" },
13
+ "gpx" => { "path" => "site/gpx_out" }
14
+ },
15
+ "privacy" => {
16
+ "start_end_range" => 200
17
+ },
18
+ "system" => {
19
+ "units" => "metric",
20
+ "log_level" => "info",
21
+ "timezone" => "Asia/Shanghai"
22
+ }
23
+ }.freeze
24
+
25
+ DEFAULT_PATH = "config/config.yml"
26
+ KNOWN_TOP_KEYS = %w[database sync export privacy system].freeze
27
+
28
+ def initialize(path = nil)
29
+ @data = deep_dup(DEFAULTS)
30
+ load_file(path) if path
31
+ load_file(DEFAULT_PATH) if !path && File.exist?(DEFAULT_PATH)
32
+ load_env
33
+ validate!
34
+ end
35
+
36
+ def [](key)
37
+ @data[key]
38
+ end
39
+
40
+ def dig(*keys)
41
+ @data.dig(*keys)
42
+ end
43
+
44
+ def sync_config(source)
45
+ @data.dig("sync", source) || {}
46
+ end
47
+
48
+ def privacy_config
49
+ @data["privacy"] || {}
50
+ end
51
+
52
+ def export_config
53
+ @data["export"] || {}
54
+ end
55
+
56
+ def db_path
57
+ @data.dig("database", "path") || DEFAULTS["database"]["path"]
58
+ end
59
+
60
+ private
61
+
62
+ def load_env
63
+ @data["database"]["path"] = ENV["GIT_FIT_DATABASE_PATH"] if ENV["GIT_FIT_DATABASE_PATH"]
64
+
65
+ GitFit.registered_configs.each do |reg|
66
+ reg[:keys].each do |key|
67
+ env_name = "#{reg[:prefix]}_#{key.to_s.upcase}"
68
+ next unless ENV.key?(env_name)
69
+ set_nested(@data, reg[:config_path] + [key.to_s], ENV[env_name])
70
+ end
71
+ end
72
+ end
73
+
74
+ def validate!
75
+ @data.each_key do |k|
76
+ next if k.empty? || KNOWN_TOP_KEYS.include?(k)
77
+ warn "Config: unknown top-level key '#{k}'"
78
+ end
79
+ end
80
+
81
+ def load_file(path)
82
+ yaml = YAML.safe_load(File.read(path)) || {}
83
+ deep_merge!(@data, yaml)
84
+ rescue => e
85
+ warn "Config: failed to load #{path}: #{e.message}"
86
+ end
87
+
88
+ def set_nested(hash, keys, value)
89
+ last = keys.pop
90
+ keys.each { |k| hash[k] ||= {}; hash = hash[k] }
91
+ hash[last] = typed_value(value)
92
+ end
93
+
94
+ def typed_value(str)
95
+ case str
96
+ when /\A\d+\z/ then str.to_i
97
+ when /\A\d+\.\d+\z/ then str.to_f
98
+ when /\Atrue\z/i then true
99
+ when /\Afalse\z/i then false
100
+ when /\Anil\z/i then nil
101
+ else str
102
+ end
103
+ end
104
+
105
+ def deep_merge!(target, source)
106
+ source.each do |key, val|
107
+ if val.is_a?(Hash) && target[key].is_a?(Hash)
108
+ deep_merge!(target[key], val)
109
+ else
110
+ target[key] = deep_dup(val)
111
+ end
112
+ end
113
+ target
114
+ end
115
+
116
+ def deep_dup(obj)
117
+ case obj
118
+ when Hash then obj.each_with_object({}) { |(k, v), h| h[k] = deep_dup(v) }
119
+ when Array then obj.map { |v| deep_dup(v) }
120
+ else obj
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,44 @@
1
+ module GitFit
2
+ class ConfigTemplate
3
+ def self.generate
4
+ <<~YAML
5
+ ---
6
+ database:
7
+ path: db/fit.sqlite3
8
+
9
+ sync:
10
+ # strava: # env: GIT_FIT_STRAVA_CLIENT_ID
11
+ # client_id: "" # env: GIT_FIT_STRAVA_CLIENT_SECRET
12
+ # refresh_token: "" # env: GIT_FIT_STRAVA_REFRESH_TOKEN
13
+
14
+ # garmin: # env: GIT_FIT_GARMIN_EMAIL
15
+ # email: "" # env: GIT_FIT_GARMIN_PASSWORD
16
+ # password: ""
17
+
18
+ # keep: # env: GIT_FIT_KEEP_PHONE
19
+ # phone: "" # env: GIT_FIT_KEEP_PASSWORD
20
+ # password: ""
21
+
22
+ # igpsport: # env: GIT_FIT_IGPSPORT_PHONE
23
+ # phone: "" # env: GIT_FIT_IGPSPORT_PASSWORD
24
+ # password: ""
25
+
26
+ export:
27
+ json:
28
+ path: site/activities.json
29
+
30
+ privacy:
31
+ start_end_range: 200
32
+
33
+ system:
34
+ units: metric
35
+ log_level: info
36
+ timezone: Asia/Shanghai
37
+ YAML
38
+ end
39
+
40
+ def self.hash
41
+ YAML.safe_load(generate) || {}
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ module GitFit
2
+ module Sync
3
+ class Base
4
+ def self.register_config(*keys)
5
+ return if keys.empty?
6
+
7
+ source = name.split("::").last.downcase
8
+ GitFit.register_config_source(
9
+ prefix: "GIT_FIT_#{source.upcase}",
10
+ keys: keys,
11
+ config_path: ["sync", source]
12
+ )
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module GitFit
2
+ module Sync
3
+ class Strava < Base
4
+ register_config :client_id, :client_secret, :refresh_token
5
+ end
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module GitFit
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-fit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lax
@@ -35,6 +35,10 @@ files:
35
35
  - exe/git-fit
36
36
  - lib/git-fit.rb
37
37
  - lib/git_fit/cli.rb
38
+ - lib/git_fit/config.rb
39
+ - lib/git_fit/config_template.rb
40
+ - lib/git_fit/sync/base.rb
41
+ - lib/git_fit/sync/strava.rb
38
42
  - lib/git_fit/version.rb
39
43
  homepage: https://github.com/Lax/git-fit
40
44
  licenses: