csv_row_model 0.3.10 → 0.4.0

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
  SHA1:
3
- metadata.gz: c16a446fafcc7457fffac11919745acfa9f283cd
4
- data.tar.gz: ea6cdbed5009681cc3a8b92452f5b94bfc722640
3
+ metadata.gz: d13ce436eddba3a6cd5ad084cbd534098637279b
4
+ data.tar.gz: c0132db76eb2855b4fc0eddbe288a54e741e0544
5
5
  SHA512:
6
- metadata.gz: 6d3e8e9b72a3614bfe036917d2f724802ed8c1f9f42b0013273f761a6b9d850075ff52c2744c520b5a9159410fc99f9d87be15c12d4d614b9938feabf47c0dcf
7
- data.tar.gz: 49ab3a6bfbb1b34c293fb309b1f5cea4ea694ec667884ce39334e9d247dad7f51a867f728a1abe451c0b25f3de5a8d6625b2a602d35ba6e08d4afd9f5dd80d23
6
+ metadata.gz: 92c5b48c05ca65a73f997061a0db142b9cb684940b6adca4dee691d33b00b0bb115d4db05d6375da7e7b870c5334c667f95f4b7d5525d6c74c4eee44042d08a1
7
+ data.tar.gz: 4885e9fc4282bc8974ed6fe3f7db11c4f39875b142a0e0daca002341aff015c5f68d25017727072369592f6b6a7f50ad460c6d86cbdb7839dedad8bcf0442366
data/README.md CHANGED
@@ -8,8 +8,10 @@ First define your schema:
8
8
  class ProjectRowModel
9
9
  include CsvRowModel::Model
10
10
 
11
- column :id
11
+ column :id, options
12
12
  column :name
13
+
14
+ merge_options :id, more_options # optional
13
15
  end
14
16
  ```
15
17
 
@@ -4,10 +4,7 @@ module CsvRowModel
4
4
  module Concerns
5
5
  module InheritedClassVar
6
6
  extend ActiveSupport::Concern
7
-
8
- included do
9
- include InvalidOptions
10
- end
7
+ include InvalidOptions
11
8
 
12
9
  class_methods do
13
10
  # Clears the cache for a variable
@@ -1,39 +1,17 @@
1
+ require 'csv_row_model/export/base'
1
2
  require 'csv_row_model/export/dynamic_columns'
2
3
  require 'csv_row_model/export/attributes'
4
+ require 'csv_row_model/model/comparison'
3
5
 
4
6
  module CsvRowModel
5
7
  # Include this to with {Model} to have a RowModel for exporting to CSVs.
6
8
  module Export
7
9
  extend ActiveSupport::Concern
8
10
 
9
- included do
10
- include Attributes
11
- include DynamicColumns
11
+ include Base
12
+ include Attributes
13
+ include DynamicColumns
12
14
 
13
- attr_reader :source_model, :context
14
- validates :source_model, presence: true
15
- end
16
-
17
- # @param [Model] source_model object to export to CSV
18
- # @param [Hash] context
19
- def initialize(source_model, context={})
20
- @source_model = source_model
21
- @context = OpenStruct.new(context)
22
- end
23
-
24
- def to_rows
25
- [to_row]
26
- end
27
-
28
- # @return [Array] an array of public_send(column_name) of the CSV model
29
- def to_row
30
- formatted_attributes.values
31
- end
32
-
33
- class_methods do
34
- def setup(csv, context={}, with_headers: true)
35
- csv << headers(context) if with_headers
36
- end
37
- end
15
+ include Model::Comparison # can't be added on Model module because Model does not have attributes implemented
38
16
  end
39
17
  end
@@ -1,12 +1,9 @@
1
- require 'csv_row_model/model/comparison'
2
-
3
1
  module CsvRowModel
4
2
  module Export
5
3
  module Attributes
6
4
  extend ActiveSupport::Concern
7
5
 
8
6
  included do
9
- include Model::Comparison
10
7
  self.column_names.each { |*args| define_attribute_method(*args) }
11
8
  end
12
9
 
@@ -32,6 +29,14 @@ module CsvRowModel
32
29
  end
33
30
 
34
31
  class_methods do
32
+ # Safe to override. Method applied to each cell by default
33
+ #
34
+ # @param cell [Object] the cell's value
35
+ def format_cell(cell, column_name, column_index, context={})
36
+ cell
37
+ end
38
+
39
+ protected
35
40
  # See {Model#column}
36
41
  def column(column_name, options={})
37
42
  super
@@ -41,16 +46,8 @@ module CsvRowModel
41
46
  # Define default attribute method for a column
42
47
  # @param column_name [Symbol] the cell's column_name
43
48
  def define_attribute_method(column_name)
44
- define_method(column_name) do
45
- source_model.public_send(column_name)
46
- end
47
- end
48
-
49
- # Safe to override. Method applied to each cell by default
50
- #
51
- # @param cell [Object] the cell's value
52
- def format_cell(cell, column_name, column_index, context={})
53
- cell
49
+ return if method_defined? column_name
50
+ define_method(column_name) { source_model.public_send(column_name) }
54
51
  end
55
52
  end
56
53
  end
@@ -0,0 +1,34 @@
1
+ module CsvRowModel
2
+ module Export
3
+ module Base
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attr_reader :source_model, :context
8
+ validates :source_model, presence: true
9
+ end
10
+
11
+ # @param [Model] source_model object to export to CSV
12
+ # @param [Hash] context
13
+ def initialize(source_model, context={})
14
+ @source_model = source_model
15
+ @context = OpenStruct.new(context)
16
+ end
17
+
18
+ def to_rows
19
+ [to_row]
20
+ end
21
+
22
+ # @return [Array] an array of public_send(column_name) of the CSV model
23
+ def to_row
24
+ formatted_attributes.values
25
+ end
26
+
27
+ class_methods do
28
+ def setup(csv, context={}, with_headers: true)
29
+ csv << headers(context) if with_headers
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,148 +1,19 @@
1
+ require 'csv_row_model/import/base'
1
2
  require 'csv_row_model/import/attributes'
2
- require 'csv_row_model/import/presenter'
3
3
  require 'csv_row_model/import/dynamic_columns'
4
+ require 'csv_row_model/model/comparison'
4
5
 
5
6
  module CsvRowModel
6
7
  # Include this to with {Model} to have a RowModel for importing csvs.
7
8
  module Import
8
9
  extend ActiveSupport::Concern
9
10
 
10
- included do
11
- include Concerns::Inspect
12
- include Attributes
13
- include DynamicColumns
11
+ include Concerns::Inspect
14
12
 
15
- attr_reader :source_header, :source_row, :context, :index, :previous
13
+ include Base
14
+ include Attributes
15
+ include DynamicColumns
16
16
 
17
- self.column_names.each { |*args| define_attribute_method(*args) }
18
-
19
- validates :source_row, presence: true
20
- end
21
-
22
- # @param [Array] source_row the csv row
23
- # @param options [Hash]
24
- # @option options [Integer] :index index in the CSV file
25
- # @option options [Hash] :context extra data you want to work with the model
26
- # @option options [Array] :source_header the csv header row
27
- # @option options [CsvRowModel::Import] :previous the previous row model
28
- # @option options [CsvRowModel::Import] :parent if the instance is a child, pass the parent
29
- def initialize(source_row, options={})
30
- options = options.symbolize_keys.reverse_merge(context: {})
31
- @source_row, @context = source_row, OpenStruct.new(options[:context])
32
- @index, @source_header, @previous = options[:index], options[:source_header], options[:previous].try(:dup)
33
-
34
- previous.try(:free_previous)
35
- super(source_row, options)
36
- end
37
-
38
- # @return [Hash] a map of `column_name => source_row[index_of_column_name]`
39
- def mapped_row
40
- return {} unless source_row
41
- @mapped_row ||= self.class.column_names.zip(source_row).to_h
42
- end
43
-
44
- # Free `previous` from memory to avoid making a linked list
45
- def free_previous
46
- @previous = nil
47
- end
48
-
49
- # @return [Presenter] the presenter of self
50
- def presenter
51
- @presenter ||= self.class.presenter_class.new(self)
52
- end
53
-
54
- # @return [Model::CsvStringModel] a model with validations related to Model::csv_string_model (values are from format_cell)
55
- def csv_string_model
56
- @csv_string_model ||= begin
57
- if source_row
58
- column_names = self.class.column_names
59
- hash = column_names.zip(
60
- column_names.map.with_index do |column_name, index|
61
- self.class.format_cell(source_row[index], column_name, index, context)
62
- end
63
- ).to_h
64
- else
65
- hash = {}
66
- end
67
-
68
- self.class.csv_string_model_class.new(hash)
69
- end
70
- end
71
-
72
- # Safe to override.
73
- #
74
- # @return [Boolean] returns true, if this instance should be skipped
75
- def skip?
76
- !valid? || presenter.skip?
77
- end
78
-
79
- # Safe to override.
80
- #
81
- # @return [Boolean] returns true, if the entire csv file should stop reading
82
- def abort?
83
- presenter.abort?
84
- end
85
-
86
- def valid?(*args)
87
- super
88
-
89
- proc = -> do
90
- csv_string_model.valid?(*args)
91
- errors.messages.merge!(csv_string_model.errors.messages.reject {|k, v| v.empty? })
92
- errors.empty?
93
- end
94
-
95
- if using_warnings?
96
- csv_string_model.using_warnings(&proc)
97
- else
98
- proc.call
99
- end
100
- end
101
-
102
- class_methods do
103
- # See {Model#column}
104
- def column(column_name, options={})
105
- super
106
- define_attribute_method(column_name)
107
- end
108
-
109
- # @param [Import::Csv] csv to read from
110
- # @param [Hash] context extra data you want to work with the model
111
- # @param [Import] prevuous the previous row model
112
- # @return [Import] the next model instance from the csv
113
- def next(csv, source_header, context={}, previous=nil)
114
- csv.skip_header
115
- row_model = nil
116
-
117
- loop do # loop until the next parent or end_of_file? (need to read children rows)
118
- csv.read_row
119
- row_model ||= new(csv.current_row,
120
- index: csv.index,
121
- source_header: source_header,
122
- context: context,
123
- previous: previous)
124
-
125
- return row_model if csv.end_of_file?
126
-
127
- next_row_is_parent = !row_model.append_child(csv.next_row)
128
- return row_model if next_row_is_parent
129
- end
130
- end
131
-
132
- # @return [Class] the Class of the Presenter
133
- def presenter_class
134
- @presenter_class ||= inherited_custom_class(:presenter_class, Presenter)
135
- end
136
-
137
- protected
138
- def inspect_methods
139
- @inspect_methods ||= %i[mapped_row initialized_at parent context previous].freeze
140
- end
141
-
142
- # Call to define the presenter
143
- def presenter(&block)
144
- presenter_class.class_eval(&block)
145
- end
146
- end
17
+ include Model::Comparison # can't be added on Model module because Model does not have attributes implemented
147
18
  end
148
19
  end
@@ -1,5 +1,4 @@
1
1
  require 'csv_row_model/validators/boolean_format'
2
- require 'csv_row_model/model/comparison'
3
2
 
4
3
  module CsvRowModel
5
4
  module Import
@@ -7,7 +6,7 @@ module CsvRowModel
7
6
  extend ActiveSupport::Concern
8
7
 
9
8
  included do
10
- include Model::Comparison
9
+ self.column_names.each { |*args| define_attribute_method(*args) }
11
10
  end
12
11
 
13
12
  # Classes with a validations associated with them in csv_row_model/validators
@@ -90,9 +89,22 @@ module CsvRowModel
90
89
  end
91
90
 
92
91
  protected
92
+ # See {Model#column}
93
+ def column(column_name, options={})
94
+ super
95
+ define_attribute_method(column_name)
96
+ end
97
+
98
+ def merge_options(column_name, options={})
99
+ original_options = options(column_name)
100
+ add_type_validation(column_name) if !original_options[:validate_type] && options[:validate_type]
101
+ super
102
+ end
103
+
93
104
  # Define default attribute method for a column
94
105
  # @param column_name [Symbol] the cell's column_name
95
106
  def define_attribute_method(column_name)
107
+ return if method_defined? column_name
96
108
  add_type_validation(column_name)
97
109
  define_method(column_name) { original_attribute(column_name) }
98
110
  end
@@ -0,0 +1,136 @@
1
+ require 'csv_row_model/import/presenter'
2
+
3
+ module CsvRowModel
4
+ module Import
5
+ module Base
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :source_header, :source_row, :context, :index, :previous
10
+
11
+ validates :source_row, presence: true
12
+ end
13
+
14
+
15
+ # @param [Array] source_row the csv row
16
+ # @param options [Hash]
17
+ # @option options [Integer] :index index in the CSV file
18
+ # @option options [Hash] :context extra data you want to work with the model
19
+ # @option options [Array] :source_header the csv header row
20
+ # @option options [CsvRowModel::Import] :previous the previous row model
21
+ # @option options [CsvRowModel::Import] :parent if the instance is a child, pass the parent
22
+ def initialize(source_row, options={})
23
+ options = options.symbolize_keys.reverse_merge(context: {})
24
+ @source_row, @context = source_row, OpenStruct.new(options[:context])
25
+ @index, @source_header, @previous = options[:index], options[:source_header], options[:previous].try(:dup)
26
+
27
+ previous.try(:free_previous)
28
+ super(source_row, options)
29
+ end
30
+
31
+ # @return [Hash] a map of `column_name => source_row[index_of_column_name]`
32
+ def mapped_row
33
+ return {} unless source_row
34
+ @mapped_row ||= self.class.column_names.zip(source_row).to_h
35
+ end
36
+
37
+ # Free `previous` from memory to avoid making a linked list
38
+ def free_previous
39
+ @previous = nil
40
+ end
41
+
42
+ # @return [Presenter] the presenter of self
43
+ def presenter
44
+ @presenter ||= self.class.presenter_class.new(self)
45
+ end
46
+
47
+ # @return [Model::CsvStringModel] a model with validations related to Model::csv_string_model (values are from format_cell)
48
+ def csv_string_model
49
+ @csv_string_model ||= begin
50
+ if source_row
51
+ column_names = self.class.column_names
52
+ hash = column_names.zip(
53
+ column_names.map.with_index do |column_name, index|
54
+ self.class.format_cell(source_row[index], column_name, index, context)
55
+ end
56
+ ).to_h
57
+ else
58
+ hash = {}
59
+ end
60
+
61
+ self.class.csv_string_model_class.new(hash)
62
+ end
63
+ end
64
+
65
+ # Safe to override.
66
+ #
67
+ # @return [Boolean] returns true, if this instance should be skipped
68
+ def skip?
69
+ !valid? || presenter.skip?
70
+ end
71
+
72
+ # Safe to override.
73
+ #
74
+ # @return [Boolean] returns true, if the entire csv file should stop reading
75
+ def abort?
76
+ presenter.abort?
77
+ end
78
+
79
+ def valid?(*args)
80
+ super
81
+
82
+ proc = -> do
83
+ csv_string_model.valid?(*args)
84
+ errors.messages.merge!(csv_string_model.errors.messages.reject {|k, v| v.empty? })
85
+ errors.empty?
86
+ end
87
+
88
+ if using_warnings?
89
+ csv_string_model.using_warnings(&proc)
90
+ else
91
+ proc.call
92
+ end
93
+ end
94
+
95
+ class_methods do
96
+ # @param [Import::Csv] csv to read from
97
+ # @param [Hash] context extra data you want to work with the model
98
+ # @param [Import] prevuous the previous row model
99
+ # @return [Import] the next model instance from the csv
100
+ def next(csv, source_header, context={}, previous=nil)
101
+ csv.skip_header
102
+ row_model = nil
103
+
104
+ loop do # loop until the next parent or end_of_file? (need to read children rows)
105
+ csv.read_row
106
+ row_model ||= new(csv.current_row,
107
+ index: csv.index,
108
+ source_header: source_header,
109
+ context: context,
110
+ previous: previous)
111
+
112
+ return row_model if csv.end_of_file?
113
+
114
+ next_row_is_parent = !row_model.append_child(csv.next_row)
115
+ return row_model if next_row_is_parent
116
+ end
117
+ end
118
+
119
+ # @return [Class] the Class of the Presenter
120
+ def presenter_class
121
+ @presenter_class ||= inherited_custom_class(:presenter_class, Presenter)
122
+ end
123
+
124
+ protected
125
+ def inspect_methods
126
+ @inspect_methods ||= %i[mapped_row initialized_at parent context previous].freeze
127
+ end
128
+
129
+ # Call to define the presenter
130
+ def presenter(&block)
131
+ presenter_class.class_eval(&block)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -4,10 +4,10 @@ module CsvRowModel
4
4
  module Validations
5
5
  extend ActiveSupport::Concern
6
6
 
7
- included do
8
- include ActiveModel::Validations
9
- include Validators::ValidateAttributes
7
+ include ActiveModel::Validations
8
+ include Validators::ValidateAttributes
10
9
 
10
+ included do
11
11
  validate_attributes :csv
12
12
  end
13
13
 
@@ -1,5 +1,6 @@
1
1
  require 'csv_row_model/model/csv_string_model'
2
2
 
3
+ require 'csv_row_model/model/base'
3
4
  require 'csv_row_model/model/columns'
4
5
  require 'csv_row_model/model/children'
5
6
  require 'csv_row_model/model/dynamic_columns'
@@ -9,58 +10,15 @@ module CsvRowModel
9
10
  module Model
10
11
  extend ActiveSupport::Concern
11
12
 
12
- included do
13
- include Concerns::InheritedClassVar
13
+ include Concerns::InheritedClassVar
14
14
 
15
- include ActiveWarnings
16
- include Validators::ValidateAttributes
15
+ include ActiveWarnings
16
+ include Validators::ValidateAttributes
17
17
 
18
- include Columns
19
- include Children
20
- include DynamicColumns
18
+ include Base
21
19
 
22
- # @return [Model] return the parent, if this instance is a child
23
- attr_reader :parent
24
-
25
- # @return [DateTime] return when self has been intialized
26
- attr_reader :initialized_at
27
-
28
- validate_attributes :parent
29
- end
30
-
31
- # @param [NilClass] source not used here, see {Input}
32
- # @param [Hash] options
33
- # @option options [String] :parent if the instance is a child, pass the parent
34
- def initialize(source=nil, options={})
35
- @initialized_at = DateTime.now
36
- @parent = options[:parent]
37
- end
38
-
39
- # Safe to override.
40
- #
41
- # @return [Boolean] returns true, if this instance should be skipped
42
- def skip?
43
- !valid?
44
- end
45
-
46
- # Safe to override.
47
- #
48
- # @return [Boolean] returns true, if the entire csv file should stop reading
49
- def abort?
50
- false
51
- end
52
-
53
- class_methods do
54
- # @return [Class] the Class with validations of the csv_string_model
55
- def csv_string_model_class
56
- @csv_string_model_class ||= inherited_custom_class(:csv_string_model_class, CsvStringModel)
57
- end
58
-
59
- protected
60
- # Called to add validations to the csv_string_model_class
61
- def csv_string_model(&block)
62
- csv_string_model_class.class_eval(&block)
63
- end
64
- end
20
+ include Columns
21
+ include Children
22
+ include DynamicColumns
65
23
  end
66
24
  end
@@ -0,0 +1,52 @@
1
+ module CsvRowModel
2
+ module Model
3
+ module Base
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # @return [Model] return the parent, if this instance is a child
8
+ attr_reader :parent
9
+
10
+ # @return [DateTime] return when self has been intialized
11
+ attr_reader :initialized_at
12
+
13
+ validate_attributes :parent
14
+ end
15
+
16
+ # @param [NilClass] source not used here, see {Input}
17
+ # @param [Hash] options
18
+ # @option options [String] :parent if the instance is a child, pass the parent
19
+ def initialize(source=nil, options={})
20
+ @initialized_at = DateTime.now
21
+ @parent = options[:parent]
22
+ end
23
+
24
+ # Safe to override.
25
+ #
26
+ # @return [Boolean] returns true, if this instance should be skipped
27
+ def skip?
28
+ !valid?
29
+ end
30
+
31
+ # Safe to override.
32
+ #
33
+ # @return [Boolean] returns true, if the entire csv file should stop reading
34
+ def abort?
35
+ false
36
+ end
37
+
38
+ class_methods do
39
+ # @return [Class] the Class with validations of the csv_string_model
40
+ def csv_string_model_class
41
+ @csv_string_model_class ||= inherited_custom_class(:csv_string_model_class, CsvStringModel)
42
+ end
43
+
44
+ protected
45
+ # Called to add validations to the csv_string_model_class
46
+ def csv_string_model(&block)
47
+ csv_string_model_class.class_eval(&block)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -87,12 +87,18 @@ module CsvRowModel
87
87
  # @option options [String] :header human friendly string of the column name, by default format_header(column_name)
88
88
  # @option options [Hash] :header_matchs array with string to match cell to find in the row, by default column name
89
89
  def column(column_name, options={})
90
+ column_name = column_name.to_sym
91
+
90
92
  extra_keys = options.keys - VALID_OPTIONS_KEYS
91
93
  raise ArgumentError.new("invalid options #{extra_keys}") unless extra_keys.empty?
92
94
 
93
- merge_columns(column_name.to_sym => options)
95
+ merge_columns(column_name => options)
96
+ end
97
+
98
+ def merge_options(column_name, options={})
99
+ column_name = column_name.to_sym
100
+ column(column_name, (options(column_name) || {}).merge(options))
94
101
  end
95
- # alias_method :row, :column
96
102
  end
97
103
  end
98
104
  end
@@ -2,7 +2,6 @@ module CsvRowModel
2
2
  module Model
3
3
  module DynamicColumns
4
4
  extend ActiveSupport::Concern
5
-
6
5
  included do
7
6
  inherited_class_hash :dynamic_columns
8
7
  end
@@ -4,7 +4,6 @@ module CsvRowModel
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
-
8
7
  class << self
9
8
  alias_method :row_names, :column_names
10
9
  alias_method :rows, :columns
@@ -1,3 +1,3 @@
1
1
  module CsvRowModel
2
- VERSION = "0.3.10"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_row_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.10
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Chung
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-04-06 00:00:00.000000000 Z
12
+ date: 2016-04-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -66,11 +66,13 @@ files:
66
66
  - lib/csv_row_model/engine.rb
67
67
  - lib/csv_row_model/export.rb
68
68
  - lib/csv_row_model/export/attributes.rb
69
+ - lib/csv_row_model/export/base.rb
69
70
  - lib/csv_row_model/export/dynamic_columns.rb
70
71
  - lib/csv_row_model/export/file.rb
71
72
  - lib/csv_row_model/export/file_model.rb
72
73
  - lib/csv_row_model/import.rb
73
74
  - lib/csv_row_model/import/attributes.rb
75
+ - lib/csv_row_model/import/base.rb
74
76
  - lib/csv_row_model/import/csv.rb
75
77
  - lib/csv_row_model/import/dynamic_columns.rb
76
78
  - lib/csv_row_model/import/file.rb
@@ -79,6 +81,7 @@ files:
79
81
  - lib/csv_row_model/import/file_model.rb
80
82
  - lib/csv_row_model/import/presenter.rb
81
83
  - lib/csv_row_model/model.rb
84
+ - lib/csv_row_model/model/base.rb
82
85
  - lib/csv_row_model/model/children.rb
83
86
  - lib/csv_row_model/model/columns.rb
84
87
  - lib/csv_row_model/model/comparison.rb