Class: Scriptorium::Post

Inherits:
Object
  • Object
show all
Includes:
Contract, Exceptions, Helpers
Defined in:
lib/scriptorium/post.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Contract

#assume, #check_invariants, enabled?, #invariant, #verify

Methods included from Helpers

#add_post_to_state_file, #cf_time, #change_config, #clean_slugify, #copy_gem_asset_to_user, #copy_to_clipboard, #d4, #escape_html, #find_asset, #format_date, #generate_missing_asset_svg, #get_asset_path, #get_from_clipboard, #getvars, #list_gem_assets, #make_dir, #make_tree, #need, #parse_commented_file, #post_compare, #post_in_state_file?, #read_commented_file, #read_file, #read_post_state_file, #remove_post_from_state_file, #see, #see_file, #slugify, #substitute, #system!, #tty, #view_dir, #write_file, #write_file!, #write_post_state_file, #ymdhms, #ymdhms_filename

Methods included from Exceptions

#make_exception

Constructor Details

#initialize(repo, num) ⇒ Post

Returns a new instance of Post.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/scriptorium/post.rb', line 8

def initialize(repo, num)
  assume { repo.is_a?(Scriptorium::Repo) }
  assume { num.is_a?(Integer) || num.is_a?(String) }
  validate_initialization(repo, num)
  
  @repo = repo
  @num = d4(num.to_i)  # num is zero-padded string
  @id = num.to_i       # id is integer
  @meta = nil  # Explicitly initialize for clarity
  
  # Define invariants
  invariant { @id > 0 }
  invariant { @repo.is_a?(Scriptorium::Repo) }
  invariant { @num.match?(/^\d{4}$/) }
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



6
7
8
# File 'lib/scriptorium/post.rb', line 6

def id
  @id
end

#numObject (readonly)

Returns the value of attribute num.



6
7
8
# File 'lib/scriptorium/post.rb', line 6

def num
  @num
end

#repoObject (readonly)

Returns the value of attribute repo.



6
7
8
# File 'lib/scriptorium/post.rb', line 6

def repo
  @repo
end

Class Method Details

.read(repo, num, deleted: false) ⇒ Object

New class method to read metadata and initialize the Post



196
197
198
199
200
201
# File 'lib/scriptorium/post.rb', line 196

def self.read(repo, num, deleted: false)
  post = new(repo, num)
  post.
  post.
  post
end

Instance Method Details

#attrs(*keys) ⇒ Object

New method to access multiple attributes at once



191
192
193
# File 'lib/scriptorium/post.rb', line 191

def attrs(*keys)
    keys.map { |key| send(key) }
end

#blurbObject



70
71
72
# File 'lib/scriptorium/post.rb', line 70

def blurb
  meta["post.blurb"]
end

#blurb_textObject



66
67
68
# File 'lib/scriptorium/post.rb', line 66

def blurb_text
  meta["post.blurb"]
end

#createdObject



86
87
88
# File 'lib/scriptorium/post.rb', line 86

def created
  meta["post.created"]
end

#dateObject



90
91
92
# File 'lib/scriptorium/post.rb', line 90

def date
  pubdate || created
end

#deletedObject



164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/scriptorium/post.rb', line 164

def deleted
  # Check what directory actually exists
  normal_dir = @repo.root/:posts/@num
  deleted_dir = @repo.root/:posts/"_#{@num}"
  
  if Dir.exist?(deleted_dir)
    true
  elsif Dir.exist?(normal_dir)
    false
  else
    # Neither exists - post never existed
    raise "Post directory for #{@num} not found"
  end
end

#deleted=(value) ⇒ Object



179
180
181
182
183
184
185
186
187
188
# File 'lib/scriptorium/post.rb', line 179

def deleted=(value)
  check_invariants
  assume { [true, false].include?(value) }
  
  meta["post.deleted"] = value ? "true" : "false"
  
  
  verify { meta["post.deleted"] == (value ? "true" : "false") }
  check_invariants
end

#dirObject



31
32
33
# File 'lib/scriptorium/post.rb', line 31

def dir
  repo.root/:posts/@num
end

#load_metadataObject

Additional method to load metadata explicitly, so it’s only called once



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/scriptorium/post.rb', line 230

def 
  check_invariants
  assume { File.exist?(meta_file) }
  
  @meta = {}
  @repo.tree("/tmp/tree.txt")
  read_file(meta_file, lines: true, chomp: true).each do |line|
    key, value = line.strip.split(/\s+/, 2)
    next if key.nil? || key.empty?
    @meta[key] = value
  end 
  
  verify { @meta.is_a?(Hash) }
  check_invariants
  @meta
end

#metaObject



203
204
205
206
# File 'lib/scriptorium/post.rb', line 203

def meta
  return @meta if @meta
  @meta = File.exist?(meta_file) ?  : {}
end

#meta_fileObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/scriptorium/post.rb', line 35

def meta_file
  # Check what directory actually exists
  normal_dir = @repo.root/:posts/@num
  deleted_dir = @repo.root/:posts/"_#{@num}"
  
  if Dir.exist?(normal_dir)
    # Normal post directory exists
    normal_dir/"meta.txt"
  elsif Dir.exist?(deleted_dir)
    # Deleted post directory exists
    deleted_dir/"meta.txt"
  else
    # Neither exists - post never existed
    raise "Post directory for #{@num} not found"
  end
end

#pubdateObject



82
83
84
# File 'lib/scriptorium/post.rb', line 82

def pubdate
  meta["post.pubdate"]
end

#pubdate_month_day_yearObject



140
141
142
# File 'lib/scriptorium/post.rb', line 140

def pubdate_month_day_year
  [meta["post.pubdate.month"], meta["post.pubdate.day"], meta["post.pubdate.year"]]
end

#save_metadataObject



247
248
249
250
251
252
253
254
255
256
257
# File 'lib/scriptorium/post.rb', line 247

def 
  check_invariants
  assume { @meta.is_a?(Hash) }
  assume { !@meta.empty? }
  
  lines = @meta.map { |k, v| sprintf("%-18s  %s", k, v) }
  write_file(meta_file, lines.join("\n"))
  
  verify { File.exist?(meta_file) }
  check_invariants
end

#set_pubdate(ymd, seconds: 0) ⇒ Object

Raises:

  • (TestModeOnly)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/scriptorium/post.rb', line 94

def set_pubdate(ymd, seconds: 0)
  check_invariants
  assume { Scriptorium::Repo.testing }
  assume { ymd.is_a?(String) }
  assume { seconds.is_a?(Integer) && seconds >= 0 }
  
  raise TestModeOnly unless Scriptorium::Repo.testing
  
  validate_date_format(ymd)
  
  yyyy, mm, dd = ymd.split("-")
  t = Time.new(yyyy.to_i, mm.to_i, dd.to_i, 0, 0, seconds)
  
  (t)
  
  
  verify { meta["post.pubdate"].is_a?(String) }
  check_invariants
end

#set_pubdate_with_seconds(ymd, seconds) ⇒ Object

Legacy method for backward compatibility - preserves 12:00 base time

Raises:

  • (TestModeOnly)


128
129
130
131
132
133
134
135
136
137
138
# File 'lib/scriptorium/post.rb', line 128

def set_pubdate_with_seconds(ymd, seconds)
  raise TestModeOnly unless Scriptorium::Repo.testing
  
  validate_date_format(ymd)
  
  yyyy, mm, dd = ymd.split("-")
  t = Time.new(yyyy.to_i, mm.to_i, dd.to_i, 12, 0, seconds)  # 12:00:XX for ordering
  
  (t)
  
end

#slugObject



78
79
80
# File 'lib/scriptorium/post.rb', line 78

def slug
  meta["post.slug"]
end

#tagsObject



154
155
156
# File 'lib/scriptorium/post.rb', line 154

def tags
  meta["post.tags"]
end

#tags_arrayObject



158
159
160
161
162
# File 'lib/scriptorium/post.rb', line 158

def tags_array
  tags_str = meta["post.tags"]
  return [] if tags_str.nil? || tags_str.strip.empty?
  tags_str.strip.split(/,\s*/)
end

#titleObject



74
75
76
# File 'lib/scriptorium/post.rb', line 74

def title
  meta["post.title"]
end

#update_pubdate_metadata(time) ⇒ Object



120
121
122
123
124
125
# File 'lib/scriptorium/post.rb', line 120

def (time)
  meta["post.pubdate"] = time.strftime("%Y-%m-%d %H:%M:%S") 
  meta["post.pubdate.month"] = time.strftime("%B") 
  meta["post.pubdate.day"] = time.strftime("%e") 
  meta["post.pubdate.year"] = time.strftime("%Y") 
end

#validate_metadata_consistencyObject



52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/scriptorium/post.rb', line 52

def 
  return unless File.exist?(meta_file)
  
  normal_dir = @repo.root/:posts/@num
  deleted_dir = @repo.root/:posts/"_#{@num}"
   = meta["post.deleted"] == "true"
  
  if Dir.exist?(normal_dir) && 
    raise "Inconsistency: Post #{@num} has normal directory but metadata shows deleted=true"
  elsif Dir.exist?(deleted_dir) && !
    raise "Inconsistency: Post #{@num} has deleted directory but metadata shows deleted=false"
  end
end

#varsObject



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/scriptorium/post.rb', line 208

def vars
  return @vars if defined?(@vars)
  @vars = Hash.new("")
  meta.each_pair {|k,v| @vars[k.to_sym] = v }
  
  # Add computed date components for template access
  date_value = pubdate || created
  if date_value
    begin
      date_obj = Date.parse(date_value)
      @vars[:"post.pubdate.month"] = date_obj.strftime("%B")
      @vars[:"post.pubdate.day"] = date_obj.strftime("%e").strip
      @vars[:"post.pubdate.year"] = date_obj.strftime("%Y")
    rescue Date::Error
      # If date is invalid, leave the computed fields empty
    end
  end
  
  @vars
end

#viewsObject



144
145
146
# File 'lib/scriptorium/post.rb', line 144

def views
  meta["post.views"]
end

#views_arrayObject



148
149
150
151
152
# File 'lib/scriptorium/post.rb', line 148

def views_array
  views_str = meta["post.views"]
  return [] if views_str.nil? || views_str.strip.empty?
  views_str.strip.split(/\s+/)
end