Class: Fe::Extractor

Inherits:
Object
  • Object
show all
Defined in:
lib/fe/extractor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#extract_codeObject

Returns the value of attribute extract_code.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def extract_code
  @extract_code
end

#input_arrayObject

Returns the value of attribute input_array.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def input_array
  @input_array
end

#manifest_hashObject

Returns the value of attribute manifest_hash.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def manifest_hash
  @manifest_hash
end

#nameObject

Returns the value of attribute name.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def name
  @name
end

#row_countsObject

Returns the value of attribute row_counts.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def row_counts
  @row_counts
end

#table_name_to_model_name_hashObject

Returns the value of attribute table_name_to_model_name_hash.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def table_name_to_model_name_hash
  @table_name_to_model_name_hash
end

#table_namesObject

Returns the value of attribute table_names.



3
4
5
# File 'lib/fe/extractor.rb', line 3

def table_names
  @table_names
end

Instance Method Details

#extractObject

PUBLIC API #



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/fe/extractor.rb', line 15

def extract
  load_input_array_by_executing_extract_code
  @row_counts = {}
  @table_names = {}
  self.output_hash.each_pair do |key,records|
    @row_counts[key] = records.length
    @table_names[key] = key.constantize.table_name
  end

  if File.directory?(self.target_path)
    FileUtils.remove_dir(self.target_path,:force => true)
  end
  FileUtils.mkdir_p(self.target_path)
  @manifest_hash = {:extract_code => self.extract_code,
                    :name => self.name,
                    :model_names => self.model_names,
                    :row_counts => self.row_counts,
                    :table_names => self.models.map {|m| m.table_name},
                    :table_name_to_model_name_hash => self.models.inject({}) {|h,m| h[m.table_name] = m.to_s; h }
                   }
  File.open(self.manifest_file_path,'w') do |file|
    file.write(@manifest_hash.to_yaml)
  end
  self.write_model_fixtures
end

#fixture_hash_for_model(model_name) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/fe/extractor.rb', line 161

def fixture_hash_for_model(model_name)
  model_name = model_name.to_s
  if @fixture_hashes.nil?
    @fixture_hashes = {}
  end
  if @fixture_hashes.has_key?(model_name) 
    @fixture_hashes[model_name]
  else
    @fixture_hashes[model_name] = YAML.load_file(self.fixture_path_for_model(model_name))
  end
  @fixture_hashes[model_name]
end

#fixture_path_for_model(model_name) ⇒ Object



157
158
159
# File 'lib/fe/extractor.rb', line 157

def fixture_path_for_model(model_name)
  File.join(self.target_path,"#{model_name.constantize.table_name}.yml")
end

#load_from_args(active_relation_or_array, *args) ⇒ Object

OVERLOADED CONSTRUCTORS #

  • These are used by the Fe module to setup the Extractor object

This is called from 2 types of invocations

Fe.extract('Post.all', :name => :bla)
or 
Fe.extract('[Post.all,Comment.all]', :name => :bla2)


185
186
187
188
189
190
191
192
193
# File 'lib/fe/extractor.rb', line 185

def load_from_args(active_relation_or_array,*args)
  options = args.extract_options!
  @name = (options[:name] || Time.now.strftime("%Y_%m_%d_%H_%M_%S")).to_sym
  if active_relation_or_array.kind_of? String
    @extract_code = active_relation_or_array 
  else
    raise "Extract code must be a string, so .rebuild can be called"
  end
end

#load_from_manifestObject



199
200
201
202
203
204
205
206
207
208
# File 'lib/fe/extractor.rb', line 199

def load_from_manifest
  raise "u gotta set .name to use this method" if self.name.blank?
  @manifest_hash = YAML.load_file(self.manifest_file_path)
  @extract_code = @manifest_hash[:extract_code]
  @name = @manifest_hash[:name]
  @models = @manifest_hash[:model_names].map {|x| x.constantize}
  @row_counts = @manifest_hash[:row_counts]
  @table_names = @manifest_hash[:table_names]
  @table_name_to_model_name_hash = @manifest_hash[:table_name_to_model_name_hash]
end

#load_input_array_by_executing_extract_codeObject



195
196
197
# File 'lib/fe/extractor.rb', line 195

def load_input_array_by_executing_extract_code
  @input_array = Array(eval(@extract_code)).to_a
end

#load_into_database(options = {}) ⇒ Object

Loads data from each fixture file in the extract set using ActiveRecord::Fixtures



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/fe/extractor.rb', line 44

def load_into_database(options={})
  # necessary to make multiple invocations possible in a single test
  # case possible
  ActiveRecord::Fixtures.reset_cache

  # Filter down the models to load if specified
  the_tables = if options.has_key?(:only)
    self.table_names.select {|x| Array(options[:only]).include?(x) }
  elsif options.has_key?(:except)
    self.table_names.select {|x| !Array(options[:except]).include?(x) }
  else
    self.table_names
  end
  raise "No models to load, relax your :only or :except filters (or don't bother calling this method)" if the_tables.empty?

  the_tables.each do |table_name|
    class_name = if self.table_name_to_model_name_hash.kind_of?(Hash)
      self.table_name_to_model_name_hash[table_name]
    else
      ActiveSupport::Deprecation.warn "your fe_manifest.yml does not contain a table_name_to_model_name_hash (as found in 1.0.0 or earlier). Version 2.0.0 will require this. See test cases for how to manually jigger your fe_manifest.ymls to function."
      nil
    end
    if options[:map].nil?
      # Vanilla create_fixtures will work fine when no mapping is being used
      ActiveRecord::Fixtures.create_fixtures(self.target_path, table_name)
    else
      # Map table_name via a function (great for prefixing)
      new_table_name = if options[:map].kind_of?(Proc)
        options[:map].call(table_name)
      # Map table_name via a Hash table name mapping
      elsif options[:map][table_name].kind_of? String
        options[:map][table_name]
      else
        table_name # No mapping for this table name
      end
      fixtures = ActiveRecord::Fixtures.new( ActiveRecord::Base.connection,
          new_table_name,
          class_name,
          ::File.join(self.target_path, table_name))
      fixtures.table_rows.each do |the_table_name,rows|
        rows.each do |row|
          ActiveRecord::Base.connection.insert_fixture(row, the_table_name)
        end
      end
    end
    # FIXME: The right way to do this is to fork the oracle enhanced adapter
    # and implement a reset_pk_sequence! method, this is what ActiveRecord::Fixtures
    # calls.  aka this code should be eliminated/live elsewhere.
    case ActiveRecord::Base.connection.adapter_name
    when /oracle/i
      model = class_name.constantize
      if model.column_names.include? "id"
        sequence_name = model.sequence_name.to_s
        max_id = model.maximum(:id)
        next_id = max_id.nil? ? 1 : max_id.to_i + 1
        begin
          ActiveRecord::Base.connection.execute("drop sequence #{sequence_name}")
        rescue
          puts "[Iron Fixture Extractor] WARNING: couldnt drop the sequence #{sequence_name}, (but who cares!)"
        end
        begin
          q="create sequence #{sequence_name} increment by 1 start with #{next_id}"
          ActiveRecord::Base.connection.execute(q)
        rescue
          puts "[Iron Fixture Extractor] WARNING: couldnt create the sequence #{sequence_name}"
        end
      end
    else
      # Do nothing, only oracle adapters need this
    end
  end
end

#manifest_file_pathObject



153
154
155
# File 'lib/fe/extractor.rb', line 153

def manifest_file_path
  File.join(self.target_path,'fe_manifest.yml')
end

#model_namesObject



139
140
141
# File 'lib/fe/extractor.rb', line 139

def model_names
  self.output_hash.keys
end

#modelsObject

Note: this behavior is different if load_from_manifest vs load_from_args



145
146
147
# File 'lib/fe/extractor.rb', line 145

def models
  @models ||= self.model_names.map {|x| x.constantize}
end

#output_hashObject

Returns a hash with model class names for keys and Set’s of AR instances for values aka like this

{'Post' => [<#Post id:1>,<#Post id:2>],
 'Comment' => [<#Comment id:1>,<#Comment id:2>]}


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/fe/extractor.rb', line 123

def output_hash
  if @output_hash.blank?
    @output_hash = {}
    self.input_array.each do |t|
      if t.kind_of?(Array) || t.kind_of?(ActiveRecord::Relation)
        t.each do |ar_object|
          recurse(ar_object)
        end
      else
        recurse(t)
      end
    end
  end
  @output_hash
end

#target_pathObject



149
150
151
# File 'lib/fe/extractor.rb', line 149

def target_path
  File.join(Fe.fixtures_root,self.name.to_s)
end