twinkle 0.1.0 → 0.2.1

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: a8ac554f1dff3ef39f4915f981ec076b50702272d4e580fd53c883065b01c430
4
- data.tar.gz: dc5f679b7366c31e316f067886298f5408b32d11706bdd56de76a95ad6550685
3
+ metadata.gz: 745ebe3beb30463d26dc3801ec74938b03ae089d267586d90ff736872a5bcbca
4
+ data.tar.gz: 678c0fadd35d1798fd7f1f69fc1ba9d170f7f12c3b3099311d96a1fd56f7bae8
5
5
  SHA512:
6
- metadata.gz: 37eaf8576e4d1b47332eb5fd48c082cb08b75df2578bba102308d5d74e550e3964bbae168a462297e4af2b78f9f78d5a9f0cbe1c2041cc9236f38b13d8ebeaa2
7
- data.tar.gz: 9fcf89b14a7a6b945fe607af393db115f79d28b29a4d33585844b82009455103871060dffcc41e0017ef597ef6a05a92091410fd012fc30964fb7d1156a25181
6
+ metadata.gz: a80e569a9be8977f3447583de2e030ccf45341a25bc60a845b02766be74b7dadf19f4aa5314d6dcdd2f935d44efadf2c668ea5f43c55cc0ce7c10115bd3d14d3
7
+ data.tar.gz: a11ac6363d06444ef8aa07ca5173e8c1770ce44295ab89ca32505c0140565244b2a118a2bdac1ccccf546cd04d9ac1e8dea0040c33649691846bbad606c4de1c
data/README.md CHANGED
@@ -46,10 +46,37 @@ mount Twinkle::Engine => "/"
46
46
 
47
47
  This will mount the appcast routes at /updates/:app.slug
48
48
 
49
+ ## Extending Twinkle Apps
50
+
51
+ You can extend the Twinkle::App model by creating app/models/twinkle/app.rb
52
+
53
+ ```ruby
54
+ class Twinkle::App < ApplicationRecord
55
+ include Twinkle::Concerns::Models::App
56
+ include Summarize
57
+
58
+ # Your custom app code and validations etc go here
59
+ end
60
+ ```
61
+
49
62
  ## Contributing
50
63
 
51
64
  Pull requests welcome.
52
65
 
66
+ ## Testing
67
+
68
+ ```bash
69
+ bin/rails db:test:prepare
70
+ bin/test
71
+ ```
72
+
73
+ ## Building the gem
74
+
75
+ ```bash
76
+ gem build
77
+ gem push twinkle-x.x.x.gem
78
+ ```
79
+
53
80
  ## License
54
81
 
55
82
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,7 +1,7 @@
1
1
  module Twinkle
2
2
  class AppcastController < ApplicationController
3
3
  def show
4
- @app = App.with_versions.find_by!(slug: params[:slug])
4
+ @app = App.includes(:versions).find_by!(slug: params[:slug])
5
5
  Event.create(app: @app, **event_params)
6
6
  render layout: false, formats: :xml
7
7
  end
@@ -4,7 +4,7 @@ module Summarize
4
4
  included do
5
5
  def summarize_events(period, start_date, end_date)
6
6
  # Find or create a summary for the week
7
- summary = Twinkle::Summary.with_datapoints.find_or_create_by(app: self, period: period, start_date: start_date, end_date: end_date)
7
+ summary = Twinkle::Summary.find_or_create_by(app: self, period: period, start_date: start_date, end_date: end_date)
8
8
  data_hash = get_data_hash(start_date, end_date)
9
9
  datapoints = get_datapoints(summary, data_hash)
10
10
  save_summary(datapoints)
@@ -17,7 +17,7 @@ module Summarize
17
17
  events.created_between(start_date, end_date).find_each do |event|
18
18
  data_hash['users']['sessions'] = (data_hash.dig('users', 'sessions') || 0) + 1
19
19
  Twinkle::Event.fields.each do |field|
20
- data_hash[field][event[field]] = (data_hash.dig(field, event[field]) || 0) + 1
20
+ data_hash[field][event[field]] = (data_hash.dig(field, event[field]) || 0) + 1 if event[field].present?
21
21
  end
22
22
  end
23
23
  data_hash
@@ -1,19 +1,6 @@
1
1
  module Twinkle
2
2
  class App < ApplicationRecord
3
+ include Twinkle::Concerns::Models::App
3
4
  include Summarize
4
-
5
- has_many :versions, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Version'
6
- has_many :events, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Event'
7
- has_many :summaries, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Summary'
8
- has_one_attached :icon
9
-
10
- scope :with_versions, -> { includes(:versions).order('twinkle_versions.build desc') }
11
- scope :with_latest_version, -> { includes(:versions).order('twinkle_versions.build desc').limit(1) }
12
- scope :with_latest_summary, -> { includes(:summaries).order('twinkle_summaries.created_at desc').limit(1) }
13
- scope :with_summaries_since, ->(date) { includes(:summaries).where('twinkle_summaries.start >= ?', date).order('twinkle_summaries.start asc') }
14
-
15
- validates :name, presence: true
16
- validates :slug, presence: true, uniqueness: true
17
- validates :description, presence: true
18
5
  end
19
6
  end
@@ -1,10 +1,13 @@
1
1
  module Twinkle
2
2
  class Summary < ApplicationRecord
3
3
  belongs_to :app, foreign_key: :twinkle_app_id, class_name: 'Twinkle::App'
4
- has_many :datapoints, foreign_key: :twinkle_summary_id, class_name: 'Twinkle::Datapoint'
4
+ has_many :datapoints, foreign_key: :twinkle_summary_id, class_name: 'Twinkle::Datapoint', dependent: :delete_all
5
5
 
6
+ # Virtual relationships
7
+ has_one :latest_user_summary, -> { where(name: 'users').order('twinkle_datapoints.value desc').limit(1) }, foreign_key: :twinkle_summary_id, class_name: 'Twinkle::Datapoint'
8
+
9
+ scope :since, ->(date) { where('twinkle_summaries.start_date >= ?', date).order('twinkle_summaries.start_date asc') }
6
10
  scope :week_of, -> (start_date, end_date) { where(period: :week).where("start_date >= ? AND end_date <= ?", start_date, end_date) }
7
- scope :with_datapoints, -> { includes(:datapoints) }
8
11
 
9
12
  enum period: { week: 0, fortnight: 1, month: 2 }
10
13
 
@@ -5,27 +5,40 @@ module Twinkle
5
5
  belongs_to :app, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::App'
6
6
 
7
7
  validates :number, presence: true
8
- validates :build, presence: true
9
- validates :description, presence: true
10
- validates :binary_url, presence: true
11
- validates :length, presence: true
12
- validates :length, numericality: { only_integer: true, greater_than: 0 }
8
+ validates :build, numericality: { only_integer: true, greater_than: 0 }
9
+ validates :description, presence: true, if: -> { published }
10
+ validates :binary_url, presence: true, if: -> { published }
11
+ validates :length, presence: true, if: -> { published }
12
+ validates :length, numericality: { only_integer: true, greater_than: 0 }, allow_blank: true
13
+ validates :phased_rollout_interval, numericality: { only_integer: true, greater_than: 0 }, allow_blank: true
13
14
 
14
15
  # validates that the binary_url is a valid URL
15
16
  validate :binary_url_is_url
16
17
 
18
+ # validates that the release_notes_link is a valid URL
19
+ validate :release_notes_link_is_url
20
+
21
+ # validates that the full_release_notes_link is a valid URL
22
+ validate :full_release_notes_link_is_url
23
+
17
24
  # validates that one of the two signatures is present
18
- validate :signature_present
25
+ validate :signature_present, if: -> { published }
19
26
 
20
27
  private
21
28
 
22
29
  def binary_url_is_url
23
- begin
24
- uri = URI.parse(binary_url)
25
- raise URI::InvalidURIError unless uri.is_a?(URI::HTTP) && uri.host.present?
26
- rescue URI::InvalidURIError
27
- errors.add(:binary_url, "is not a valid URL")
28
- end
30
+ return true if binary_url.blank?
31
+ errors.add(:binary_url, "is not a valid URL") unless is_url?(binary_url)
32
+ end
33
+
34
+ def release_notes_link_is_url
35
+ return true if release_notes_link.blank?
36
+ errors.add(:release_notes_link, "is not a valid URL") unless is_url?(release_notes_link)
37
+ end
38
+
39
+ def full_release_notes_link_is_url
40
+ return true if full_release_notes_link.blank?
41
+ errors.add(:full_release_notes_link, "is not a valid URL") unless is_url?(full_release_notes_link)
29
42
  end
30
43
 
31
44
  def signature_present
@@ -33,5 +46,13 @@ module Twinkle
33
46
  errors.add(:base, "At least one of the two signatures must be present")
34
47
  end
35
48
  end
49
+
50
+ private
51
+ def is_url?(url)
52
+ uri = URI.parse(url)
53
+ uri.is_a?(URI::HTTP) && uri.host.present?
54
+ rescue URI::InvalidURIError
55
+ false
56
+ end
36
57
  end
37
58
  end
@@ -5,14 +5,54 @@
5
5
  <% @app.versions.each do |version| %>
6
6
  <item>
7
7
  <title><%= version.number %></title>
8
- <pubDate><%= version.inserted_at %></pubDate>
8
+ <pubDate><%= version.published_at.strftime("%a, %d %b %Y %H:%M:%S %z") %></pubDate>
9
9
  <sparkle:version><%= version.build %></sparkle:version>
10
10
  <sparkle:shortVersionString><%= version.number %></sparkle:shortVersionString>
11
+ <% if version.link.present? %>
12
+ <link><%= version.link %></link>
13
+ <% end %>
14
+ <% if version.channel.present? %>
15
+ <sparkle:channel><%= version.channel %></sparkle:channel>
16
+ <% end %>
17
+ <% if version.release_notes_link.present? %>
18
+ <sparkle:releaseNotesLink><%= version.release_notes_link %></sparkle:releaseNotesLink>
19
+ <% end %>
20
+ <% if version.full_release_notes_link.present? %>
21
+ <sparkle:fullReleaseNotesLink><%= version.full_release_notes_link %></sparkle:fullReleaseNotesLink>
22
+ <% end %>
23
+ <% if version.min_system_version.present? %>
11
24
  <sparkle:minimumSystemVersion><%= version.min_system_version %></sparkle:minimumSystemVersion>
25
+ <% end %>
26
+ <% if version.max_system_version.present? %>
27
+ <sparkle:maximumSystemVersion><%= version.max_system_version %></sparkle:maximumSystemVersion>
28
+ <% end %>
29
+ <% if version.minimum_auto_update_version.present? %>
30
+ <sparkle:minimumAutoupdateVersion><%= version.minimum_auto_update_version %></sparkle:minimumAutoupdateVersion>
31
+ <% end %>
32
+ <% if version.ignore_skipped_upgrades_below_version.present? %>
33
+ <sparkle:ignoreSkippedUpgradesBelowVersion><%= version.ignore_skipped_upgrades_below_version %></sparkle:ignoreSkippedUpgradesBelowVersion>
34
+ <% end %>
35
+ <% if version.informational_update_below_version.present? %>
36
+ <sparkle:informationalUpdate>
37
+ <sparkle:belowVersion><%= version.informational_update_below_version %></sparkle:belowVersion>
38
+ </sparkle:informationalUpdate>
39
+ <% end %>
40
+ <% if version.critical %>
41
+ <% if version.sparkle_two %>
42
+ <sparkle:criticalUpdate<%= version.critical_version.present? ? raw(" sparkle:version=\"#{version.critical_version}\"") : "" %>></sparkle:criticalUpdate>
43
+ <% else %>
44
+ <sparkle:tags>
45
+ <sparkle:criticalUpdate></sparkle:criticalUpdate>
46
+ </sparkle:tags>
47
+ <% end %>
48
+ <% end %>
49
+ <% if version.phased_rollout_interval.present? %>
50
+ <sparkle:phasedRolloutInterval><%= version.phased_rollout_interval %></sparkle:phasedRolloutInterval>
51
+ <% end %>
12
52
  <description>
13
53
  <%= version.description %>
14
54
  </description>
15
- <enclosure url="<%= version.url %>" length="<%= version.length %>" type="application/octet-stream" sparkle:dsaSignature="<%= version.dsa_signature %>" sparkle:edSignature="<%= version.ed_signature %>"/>
55
+ <enclosure url="<%= version.binary_url %>" length="<%= version.length %>" type="application/octet-stream" sparkle:dsaSignature="<%= version.dsa_signature %>" sparkle:edSignature="<%= version.ed_signature %>"/>
16
56
  </item>
17
57
  <% end %>
18
58
  </channel>
@@ -12,6 +12,6 @@ class CreateTwinkleVersions < ActiveRecord::Migration[7.1]
12
12
 
13
13
  t.timestamps
14
14
  end
15
- add_index :twinkle_versions, :build
15
+ add_index :twinkle_versions, [:twinkle_app_id, :build], unique: true
16
16
  end
17
17
  end
@@ -8,6 +8,6 @@ class CreateTwinkleSummaries < ActiveRecord::Migration[7.1]
8
8
 
9
9
  t.timestamps
10
10
  end
11
- add_index :twinkle_summaries, [:period, :start_date, :end_date]
11
+ add_index :twinkle_summaries, [:period, :start_date, :end_date], unique: true
12
12
  end
13
13
  end
@@ -0,0 +1,6 @@
1
+ class AddPublishedToTwinkleVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :twinkle_versions, :published, :boolean, default: false, null: false
4
+ add_index :twinkle_versions, [:twinkle_app_id, :published]
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ class AddFieldsToTwinkleVersions < ActiveRecord::Migration[7.1]
2
+ def change
3
+ add_column :twinkle_versions, :sparkle_two, :boolean, default: true
4
+ add_column :twinkle_versions, :link, :string
5
+ add_column :twinkle_versions, :min_system_version, :string
6
+ add_column :twinkle_versions, :max_system_version, :string
7
+ add_column :twinkle_versions, :minimum_auto_update_version, :string
8
+ add_column :twinkle_versions, :ignore_skipped_upgrades_below_version, :string
9
+ add_column :twinkle_versions, :informational_update_below_version, :string
10
+ add_column :twinkle_versions, :critical, :boolean, default: false
11
+ add_column :twinkle_versions, :critical_version, :string
12
+ add_column :twinkle_versions, :phased_rollout_interval, :integer
13
+ add_column :twinkle_versions, :channel, :string
14
+ add_column :twinkle_versions, :release_notes_link, :string
15
+ add_column :twinkle_versions, :full_release_notes_link, :string
16
+ add_column :twinkle_versions, :published_at, :datetime
17
+ change_column :twinkle_versions, :build, :integer, using: 'build::integer'
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module Twinkle
2
+ module Concerns
3
+ module Models
4
+ module App
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_many :versions, -> { order('twinkle_versions.build desc') }, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Version', dependent: :destroy
9
+ has_many :events, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Event', dependent: :delete_all
10
+ has_many :summaries, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Summary', dependent: :destroy
11
+ has_one_attached :icon
12
+
13
+ # Virtual relationships
14
+ has_one :latest_version, -> { order('twinkle_versions.build desc').limit(1) }, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Version'
15
+ has_one :latest_weekly_summary, -> { where(period: :week).order('twinkle_summaries.start_date desc').limit(1) }, foreign_key: 'twinkle_app_id', class_name: 'Twinkle::Summary'
16
+
17
+ validates :name, presence: true
18
+ validates :slug, presence: true, uniqueness: true
19
+ validates :description, presence: true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Twinkle
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/twinkle.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "twinkle/version"
2
2
  require "twinkle/engine"
3
+ require "twinkle/concerns/models/app"
3
4
 
4
5
  module Twinkle
5
6
  # Your code goes here...
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twinkle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Marks
@@ -57,12 +57,16 @@ files:
57
57
  - db/migrate/20240517233457_create_twinkle_events.rb
58
58
  - db/migrate/20240517234016_create_twinkle_summaries.rb
59
59
  - db/migrate/20240518000405_create_twinkle_datapoints.rb
60
+ - db/migrate/20240530023848_add_published_to_twinkle_versions.rb
61
+ - db/migrate/20240626232135_add_fields_to_twinkle_versions.rb
60
62
  - lib/tasks/twinkle_tasks.rake
61
63
  - lib/twinkle.rb
64
+ - lib/twinkle/concerns/models/app.rb
62
65
  - lib/twinkle/engine.rb
63
66
  - lib/twinkle/version.rb
64
67
  homepage: https://github.com/imothee/twinkle
65
- licenses: []
68
+ licenses:
69
+ - MIT
66
70
  metadata:
67
71
  allowed_push_host: https://rubygems.org/
68
72
  homepage_uri: https://github.com/imothee/twinkle
@@ -83,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
87
  - !ruby/object:Gem::Version
84
88
  version: '0'
85
89
  requirements: []
86
- rubygems_version: 3.3.26
90
+ rubygems_version: 3.5.9
87
91
  signing_key:
88
92
  specification_version: 4
89
93
  summary: Twinkle is a Rails engine for hosting appcast and collecting anonymous sparkle-project