Class: Bookbinder::Package

Inherits:
Object
  • Object
show all
Defined in:
lib/bookbinder/package.rb

Direct Known Subclasses

Openbook

Defined Under Namespace

Classes: Openbook

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePackage

Returns a new instance of Package.



46
47
48
49
50
51
52
# File 'lib/bookbinder/package.rb', line 46

def initialize
  @content_root = ''
  @file_aliases = {}
  @options = {}
  @files = {}
  reset_transforms
end

Instance Attribute Details

#content_rootObject

Returns the value of attribute content_root.



3
4
5
# File 'lib/bookbinder/package.rb', line 3

def content_root
  @content_root
end

#file_aliasesObject

Returns the value of attribute file_aliases.



3
4
5
# File 'lib/bookbinder/package.rb', line 3

def file_aliases
  @file_aliases
end

#file_systemObject

Returns the value of attribute file_system.



3
4
5
# File 'lib/bookbinder/package.rb', line 3

def file_system
  @file_system
end

#mapObject

Returns the value of attribute map.



3
4
5
# File 'lib/bookbinder/package.rb', line 3

def map
  @map
end

#optionsObject

Returns the value of attribute options.



3
4
5
# File 'lib/bookbinder/package.rb', line 3

def options
  @options
end

#warningsObject (readonly)

Returns the value of attribute warnings.



4
5
6
# File 'lib/bookbinder/package.rb', line 4

def warnings
  @warnings
end

Class Method Details

.build(path) ⇒ Object

Creates a new package of this class pointing at the given path (but not reading from it automatically).



29
30
31
32
33
# File 'lib/bookbinder/package.rb', line 29

def self.build(path)
  new.tap { |pkg|
    pkg.build(path)
  }
end

.read(path) ⇒ Object

Creates a new package of this class by reading content from a path.



39
40
41
42
43
# File 'lib/bookbinder/package.rb', line 39

def self.read(path)
  new.tap { |pkg|
    pkg.read(path)
  }
end

.recognize(path) ⇒ Object

In subclasses, return true if you can handle the path. We’ll iterate through all the descendent classes of Package and return the first matching class.



11
12
13
# File 'lib/bookbinder/package.rb', line 11

def self.recognize(path)
  # IMPLEMENT IN SUBCLASSES
end

.require_transforms(path) ⇒ Object



16
17
18
# File 'lib/bookbinder/package.rb', line 16

def self.require_transforms(path)
  Dir[File.join(path, '*.rb')].each { |rb| require(rb) }
end

.transformsObject



21
22
23
# File 'lib/bookbinder/package.rb', line 21

def self.transforms
  [] # Implement in subclasses
end

Instance Method Details

#apply_transform(mthd, transform_class) ⇒ Object

Applies the given transform by instantiating the class and invoking the specified method on it. ‘mthd` should be either `to_map` or `from_map`.

Runs all dependencies (that have not already run) first.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/bookbinder/package.rb', line 220

def apply_transform(mthd, transform_class)
  run = @transformed[mthd] ||= []
  return  if run.include?(transform_class)
  run.push(transform_class)
  transform = transform_class.new
  transform.dependencies.each { |dep| apply_transform(mthd, dep) }
  if transform.respond_to?(mthd)
    # puts("[TRANSFORM] #{transform_class}##{mthd}")
    @transform = transform
    @transform_method = mthd
    @transform.send(mthd, self)
    @transform = nil
    @transform_method = nil
  end
end

#build(path) ⇒ Object

Prepares this package for mapping the contents of the given path.



58
59
60
61
# File 'lib/bookbinder/package.rb', line 58

def build(path)
  @map = {}
  @file_system = file_system_from_path(path)
end

#copy_to(path_or_file_system, content_root) ⇒ Object

Copy all components and resources in the map to the new file system, then creates a new temporary package based on this one and yields it.

We create a new temporary package rather than using the current one so that we can have a different file system (and a different content root if we want that, and fresh hashes for ‘files`, `file_aliases`, etc).



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/bookbinder/package.rb', line 102

def copy_to(path_or_file_system, content_root)
  dest_file_system = path_or_file_system
  if path_or_file_system.kind_of?(String)
    dest_file_system = file_system_from_path(dest_file_system)
  end
  copy_file = lambda { |ref|
    rsrc = file(ref['path'])
    dest_path = ref['path']
    if content_root && !content_root.empty?
      dest_path = File.join(content_root, dest_path)
      dest_path = File.expand_path(dest_path, '/')[1..-1]
    end
    rsrc.copy_to(dest_file_system, dest_path)
  }
  map['cover'].values.each(&copy_file)  if map['cover']
  map['spine'].each(&copy_file)  if map['spine']
  if map['resources']
    missing = []
    map['resources'].each { |ref|
      begin
        copy_file.call(ref)
      rescue Bookbinder::FileSystem::UnknownPath
        missing << ref
      end
    }
    missing.each { |ref|
      map['resources'].delete(ref)
      warn("Resource not found: #{ref['path']}. IGNORED.")
    }
  end
  self.class.new.tap { |pkg|
    pkg.import(@map, dest_file_system, content_root, @options)
  }
end

#export(package_class) ⇒ Object

Creates a new package of the given class, using our map and file_system, and returns it.



77
78
79
# File 'lib/bookbinder/package.rb', line 77

def export(package_class)
  package_class.new.import(map, file_system, content_root, options)
end

#file(path, root_path = @content_root) ⇒ Object

Returns a file object for the given path within this package – by default, the path is from this package’s content_root.



154
155
156
157
158
159
160
# File 'lib/bookbinder/package.rb', line 154

def file(path, root_path = @content_root)
  if full_path = file_path(path, root_path)
    @files[full_path] ||= Bookbinder::File.new(full_path, @file_system)
  else
    nil
  end
end

#file_path(path, root_path = @content_root) ⇒ Object

Returns the fully-resolved path to a file within this package, converting aliases and applying the content_root if appropriate.



166
167
168
169
170
171
172
173
174
175
# File 'lib/bookbinder/package.rb', line 166

def file_path(path, root_path = @content_root)
  path = file_aliases[path]  if file_aliases[path]
  if path.kind_of?(Symbol)
    nil
  else
    path = File.join(root_path, path)  if root_path && !root_path.empty?
    path = File.expand_path(path, '/')[1..-1]
    path
  end
end

#from_mapObject

This is the default “from_map” behavior, but feel free to override it in subclasses.



207
208
209
210
211
# File 'lib/bookbinder/package.rb', line 207

def from_map
  transforms.each { |transform_class|
    apply_transform(:from_map, transform_class)
  }
end

#if_file(*file_args) {|f| ... } ⇒ Object

Yields the file only if it exists.

Yields:

  • (f)


180
181
182
183
# File 'lib/bookbinder/package.rb', line 180

def if_file(*file_args)
  f = file(*file_args)
  yield(f)  if f && f.exists?
end

#import(map, file_system, content_root, options) ⇒ Object

Copies state: a map, a file-system, and a content root. That should be enough to be a fully independent package.



85
86
87
88
89
90
91
# File 'lib/bookbinder/package.rb', line 85

def import(map, file_system, content_root, options)
  @map = duplicate_map(map)
  @file_system = file_system
  @content_root = content_root
  @options = options.clone
  self
end

#read(path) ⇒ Object

Reads the path and generates a hash “map” of the book, returning it.



67
68
69
70
71
# File 'lib/bookbinder/package.rb', line 67

def read(path)
  build(path)
  to_map
  @map
end

#reset_transformsObject



237
238
239
240
# File 'lib/bookbinder/package.rb', line 237

def reset_transforms
  @warnings = []
  @transformed = {}
end

#save_open_filesObject

Any file that is marked ‘dirty’ (because it has been opened in ‘w’ mode) will be written out to its file-system.



256
257
258
# File 'lib/bookbinder/package.rb', line 256

def save_open_files
  @files.each_value { |file| file.save }
end

#to_mapObject

This is the default “to_map” behavior, but feel free to override it in subclasses.



197
198
199
200
201
# File 'lib/bookbinder/package.rb', line 197

def to_map
  transforms.each { |transform_class|
    apply_transform(:to_map, transform_class)
  }
end

#transformsObject

The list of transforms to be applied when mapping or writing this package.



189
190
191
# File 'lib/bookbinder/package.rb', line 189

def transforms
  self.class.transforms
end

#warn(msg) ⇒ Object



243
244
245
246
247
248
249
250
# File 'lib/bookbinder/package.rb', line 243

def warn(msg)
  @warnings.push({
    :transform => @transform.class,
    :method => @transform_method,
    :message => msg
  })
  nil
end

#write(path) ⇒ Object

Writes package to path. This is destructive! Anything already at the path will be replaced.



141
142
143
144
145
146
147
148
# File 'lib/bookbinder/package.rb', line 141

def write(path)
  tmp_path = Bookbinder::Scratch.file(path)
  dest_fs = file_system_from_path(tmp_path)
  write_to_file_system(dest_fs)
  dest_fs.close  if dest_fs.respond_to?(:close)
  FileUtils.rm_r(path)  if File.exists?(path)
  FileUtils.move(tmp_path, path)
end