king_placeholder 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ gemfile:
5
+ - Gemfile
data/README.rdoc CHANGED
@@ -1,15 +1,24 @@
1
1
  = KingPlaceholder
2
+ {<img src="https://secure.travis-ci.org/salesking/king_placeholder.png?branch=master" alt="Build Status" />}[http://travis-ci.org/salesking/king_placeholder]
3
+ This gem was extracted from SalesKing, where it does placeholders substitution
4
+ for user supplied strings in Email-, Text- and Export-Templates.
2
5
 
3
- This gem was extraced from SalesKing where it handles placeholders substitution
4
- for user supplied email-, text- and export- templates.
6
+ Placeholders are declared in each class and afterwards any strings containing
7
+ [placeholders] can be parsed in the scope of the model.
5
8
 
6
- Define the available methods in your class
9
+ Parsing is done by a simple statemachine using regex for placeholder detection.
10
+
11
+ == Usage
12
+
13
+ Define the available methods in your class with 'has_placeholders'
7
14
 
8
15
  class User
9
16
  include KingPlaceholder
10
17
  has_many :comments
11
- has_placeholders :firstname
18
+ has_one :company
19
+ has_placeholders :firstname, :email
12
20
  end
21
+
13
22
  class Comment
14
23
  include KingPlaceholder
15
24
  has_placeholders :text
@@ -17,11 +26,11 @@ Define the available methods in your class
17
26
 
18
27
  Use placeholder names in square brackets:
19
28
 
20
- @user.expand_placeholders("Hello [user.first_name]")
21
- => Hello Schorsch
22
-
29
+ @user = User.new( name: "Schorsch", email: 'a@b.com')
30
+ @user.expand_placeholders("Hey [name] your address is [email]")
31
+ => "Hey Schorsch your address is a@b.com"
23
32
 
24
- It can also handle relations and collections
33
+ Handle collections
25
34
 
26
35
  @user.expand_placeholders("[comments][text][/comments]")
27
36
  => All comment texts
@@ -29,12 +38,18 @@ It can also handle relations and collections
29
38
  @user.expand_placeholders("[comments.1.text]")
30
39
  => First comment text
31
40
 
41
+ Handle relations
42
+
43
+ @user.expand_placeholders("[company.name]")
44
+ => MyCompany
45
+
46
+ Also see specs
32
47
 
33
48
  == TODO
34
49
 
35
- This gems still relies on king_views with king_format, for money, date
50
+ This gems still relies on gem king_views with king_format, for money, date
36
51
  formatting. We will outsource king_format into its own gem and remove more
37
- SalesKing internal dependencies.
52
+ Rails and SalesKing internal dependencies.
38
53
 
39
54
  == Installation
40
55
 
@@ -42,18 +57,10 @@ Add this line to your application's Gemfile:
42
57
 
43
58
  gem 'king_placeholder'
44
59
 
45
- And then execute:
46
-
47
- $ bundle
48
-
49
60
  Or install it yourself as:
50
61
 
51
62
  $ gem install king_placeholder
52
63
 
53
- == Usage
54
-
55
- See specs
56
-
57
64
  == Contributing
58
65
 
59
66
  1. Fork it
@@ -2,6 +2,7 @@ require 'statemachine'
2
2
  require 'action_view' # king_views related suxs
3
3
  require 'action_controller' # king_views
4
4
  require 'king_views'
5
+
5
6
  module KingPlaceholder
6
7
  # Statemachine for placeholder substitution
7
8
  # The statemachine is created and its state updated from within the Context
@@ -20,7 +21,6 @@ module KingPlaceholder
20
21
  context machine_context
21
22
  end
22
23
  end
23
-
24
24
  end
25
25
 
26
26
  # Statemachine context for placeholder substitution
@@ -30,7 +30,7 @@ module KingPlaceholder
30
30
  # # send match event
31
31
  # machine.sm.match
32
32
  # machine.result
33
- class ParseContext
33
+ class Parser
34
34
  include ::KingFormat::FormattingHelper
35
35
  # reference to statemachine
36
36
  attr_accessor :sm
@@ -40,14 +40,12 @@ module KingPlaceholder
40
40
  attr_accessor :result
41
41
  # the current object
42
42
  attr_accessor :obj
43
- # hash parse options
43
+ # parse-options hash
44
44
  attr_accessor :opts
45
45
 
46
- # === Parameter
47
- # obj<Object>:: any object responding to has_placeholder
48
- # content<String>:: String containing placeholders
49
- # opts<Hash{Symbol=>Mixed}>:: parse options
50
- # :only<Array> => parse only the given placeholders
46
+ # @param [Object] obj object responding to has_placeholder
47
+ # @param [String] content containing placeholders
48
+ # @param [Hash{Symbol=>Mixed}] opts parser options (none so far)
51
49
  def initialize(obj, content, opts={})
52
50
  @sm = ParseMachine.create_with(self) # init statemachine
53
51
  @obj = obj
@@ -71,20 +69,20 @@ module KingPlaceholder
71
69
  # When finished matching, triggers finished_matching event on statemachine
72
70
  def parse
73
71
  while match = @result.match(/\[((\w|\.)+)\]/)
74
- @c_placeholder = match[0] # with brackets - current placeholder
75
- @c_field = match[1] # without brackets - current field
72
+ @placeholder = match[0] # with brackets - current placeholder
73
+ @field = match[1] # without brackets - current field
76
74
 
77
75
  check_current_prefix
78
76
 
79
- if @c_field['.']
77
+ if @field['.']
80
78
  sub_object
81
- elsif obj.respond_to?(@c_field) && ( @cur_collection = obj.send(@c_field) ).is_a?(Array)
79
+ elsif obj.respond_to?(@field) && ( @cur_collection = obj.send(@field) ).is_a?(Array)
82
80
  sub_collection
83
81
  else
84
82
  sub_string
85
83
  end
86
84
  # If placeholder still exists here, it can't be recognized, sub with error
87
- @result.gsub!(@c_placeholder, "UNKNOWN for #{obj.class.to_s}: #{@c_field}")
85
+ @result.gsub!(@placeholder, "UNKNOWN for #{obj.class.to_s}: #{@field}")
88
86
  end
89
87
  @sm.finished_matching
90
88
  end
@@ -100,35 +98,34 @@ module KingPlaceholder
100
98
  # Final destination of each placeholder in it's simple notation without
101
99
  # namespace e.g. [price_to_pay]
102
100
  def sub_string
103
- return unless obj.is_placeholder?(@c_field)
104
- # only parse some placeholders
105
- # return if opts[:only] && !opts[:only].include?(@c_field)
106
- value = strfval(obj, @c_field)
107
- @result.gsub!(@c_placeholder, value.to_s) if value
101
+ return unless obj.is_placeholder?(@field)
102
+ value = strfval(obj, @field)
103
+ @result.gsub!(@placeholder, value.to_s) if value
108
104
  end
109
105
 
110
- # Namespaced notation, for related objects. We are in invoice which belongs
111
- # to a company:
106
+ # Namespaced notation, for related objects. E.g an invoice belonging to a
107
+ # company:
112
108
  # [company.default_address.zip]
113
109
  # instead of
114
110
  # [invoice.company.default_address.zip]
115
111
  def sub_object
116
- splitted = @c_field.split('.')
117
- object_name = splitted.first # just the first
118
- field_names = splitted[1..-1] # all but the first
119
- return unless object_name && obj.respond_to?(object_name) # the field exists
112
+ splits= @field.split('.')
113
+ object_name = splits.first
114
+ field_names = splits[1..-1] # all but the first
115
+ return unless object_name && obj.respond_to?(object_name)
120
116
  object = obj.send(object_name)
121
- #check if its a collection => invoice.items and access is done by ary index items.0.name
122
- if object.is_a?(Array) && ary_index = field_names.first[/\A\d*\z/] # [items.5.name] match digit only string
117
+ # Its a collection => invoice.items and access is done by ary index:
118
+ # first item => [items.1.name]
119
+ if object.is_a?(Array) && ary_index = field_names.first[/\A\d*\z/]
123
120
  field_names.delete_at(0) # remove entry from field_names ary
124
121
  # replace with empty string if the index does not exist or obj is empty
125
- @result.gsub!(@c_placeholder, '') unless object = object[ary_index.to_i-1]
122
+ @result.gsub!(@placeholder, '') unless object = object[ary_index.to_i-1]
126
123
  end
127
124
 
128
- # Recurse and Let the referenced object do the expanding
125
+ # Recurse and let the referenced object do the expanding
129
126
  if object.respond_to?(:expand_placeholders)
130
127
  value = object.expand_placeholders("[#{field_names.join('.')}]")
131
- @result.gsub!(@c_placeholder, value)
128
+ @result.gsub!(@placeholder, value)
132
129
  end
133
130
  end
134
131
 
@@ -136,30 +133,31 @@ module KingPlaceholder
136
133
  # [/field_name]
137
134
  # e.g. [items] Item price: [price] \n [/items]
138
135
  def sub_collection
139
- if match = @result.match(/\[#{@c_field}\](.*)\[\/#{@c_field}\]/m) # the /m makes the dot match newlines, too!
136
+ if match = @result.match(/\[#{@field}\](.*)\[\/#{@field}\]/m) # the /m makes the dot match newlines, too!
140
137
  whole_group = match[0]
141
138
  inner_placeholders = match[1]
142
139
  inner_result = ''
143
- # Let the referenced object do the expanding by recursion if the collection knowns placeholders
140
+ # Let the referenced object do the expanding by recursion if the collection knows placeholders
144
141
  @cur_collection.each do |item|
145
142
  inner_result << item.expand_placeholders(inner_placeholders)
146
143
  end if @cur_collection.first.respond_to?(:expand_placeholders)
147
144
  @result.gsub!(whole_group, inner_result)
148
145
  else
149
- @result.gsub!(@c_placeholder, "END MISSING FOR #{@c_field}")
146
+ @result.gsub!(@placeholder, "END MISSING FOR #{@field}")
150
147
  end
151
148
  end
152
149
 
153
- # Checks if the current field name contains the current object's class name.
154
- # If the current class is an invoice, kick the prefix:
155
- # invoice.number => number
150
+ # Checks if the field name contains the current object's class name.
151
+ # Kick the prefix if the current class matches it, e.g:
152
+ # Inside a client object
153
+ # client.number => number
156
154
  def check_current_prefix
157
- if @c_field['.']
158
- splitted = @c_field.split('.')
159
- object_name = splitted.first
155
+ if @field['.']
156
+ splits = @field.split('.')
157
+ object_name = splits.first
160
158
  if object_name && obj.class.name == object_name.classify
161
- splitted.delete_at(0) # kick the object portion => invoice
162
- @c_field = splitted.join('.') # glue the rest back together [number]
159
+ splits.delete_at(0) # kick the object portion => invoice
160
+ @field = splits.join('.') # glue the rest back together [number]
163
161
  end
164
162
  end
165
163
  end
@@ -1,3 +1,3 @@
1
1
  module KingPlaceholder
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'king_placeholder/parse_context'
1
+ require 'king_placeholder/parser'
2
2
  require 'active_support'
3
3
  require 'active_support/version'
4
4
 
@@ -9,7 +9,7 @@ require 'active_support/version'
9
9
 
10
10
  module KingPlaceholder
11
11
 
12
- # sets :placeholders and init Class.placeholders as emtpy array on inclusion
12
+ # sets :placeholders and init Class.placeholders array on inclusion
13
13
  def self.included(base)
14
14
  if ActiveSupport::VERSION::MAJOR == 3 && ActiveSupport::VERSION::MINOR > 0
15
15
  base.class_attribute :placeholders
@@ -21,26 +21,18 @@ module KingPlaceholder
21
21
  end
22
22
 
23
23
  module ClassMethods
24
-
25
- # Defines the fields returned by self.placeholders.
26
- # Sets self.publish if empty.
27
- # ==== Parameter
28
- # fieldnames<Array[Symbol]>:: the names of the fields which are available
29
- # throught the placeholder methods
24
+ # Define fields(methods) available to placeholder substitution
25
+ # @param [Array[<Symbol>] fieldnames
30
26
  def has_placeholders(*fieldnames)
31
27
  self.placeholders = fieldnames
32
28
  include InstanceMethods
33
29
  end
34
- end #ClassMethods
30
+ end
35
31
 
36
32
  module InstanceMethods
37
-
38
33
  # Check if a given field is declared as placeholder
39
- # TODO check usage and/or move to class methods
40
- # ==== Parameter
41
- # fieldname<String|Symbol>:: the field to search in the placeholders array
42
- # ==== Returns
43
- # <Boolean>:: true if in
34
+ # @param [Object] fieldname to search in placeholders array
35
+ # @return [Boolean]true if available
44
36
  def is_placeholder?(fieldname)
45
37
  self.class.placeholders.include?(fieldname.to_sym)
46
38
  end
@@ -50,11 +42,11 @@ module KingPlaceholder
50
42
  # and returns data with the same data type e.g. if you put a hash, you will
51
43
  # get a hash.
52
44
  #
53
- # ==== Examples
45
+ # @examples
54
46
  #
55
47
  # Placeholders in text strings can be written in different notations.
56
48
  #
57
- # ===== Simple Notation:
49
+ # === Simple Notation:
58
50
  #
59
51
  # => [first_name]
60
52
  # The fieldname is directly looked up on the current class:
@@ -63,7 +55,7 @@ module KingPlaceholder
63
55
  # invoice.expand_placeholders(["You owe me [price_to_pay]", "Invoice Nr. [number]"])
64
56
  # => ["You owe me 495,00 EUR", "Invoice Nr. 123"]
65
57
  #
66
- # ===== Namespaced Notation
58
+ # === Namespaced Notation
67
59
  #
68
60
  # => [company.organisation]
69
61
  # If the prefix equals the type of the current object the field is looked up on it.
@@ -78,7 +70,7 @@ module KingPlaceholder
78
70
  # invoice.expand_placeholders("[client.company.default_address.zip]")
79
71
  # => "50999"
80
72
  #
81
- # ===== Namespaced Notation with multiple related objects
73
+ # === Namespaced Notation with multiple related objects
82
74
  #
83
75
  # In a has_many relationship, related objects reside in an array, which can
84
76
  # be reached using two different strategies.
@@ -91,11 +83,9 @@ module KingPlaceholder
91
83
  # invoice.expand_placeholders("You bought an [items.0.name] for [items.0.price]")
92
84
  # => "You bought an Apple for 12 EUR"
93
85
  #
94
- # === Parameter
95
- # content<Hash,Array, String>:: Collection, Hash or single string containing
96
- # placeholders
97
- # === Returns
98
- # <Hash,Array, String>:: whatever type you threw in is also returned
86
+ # @param [Hash|Array|String] content with placeholders
87
+ # @param [Object] opts - unused for now
88
+ # @return [Hash|Array|String] whatever type you throw in is also returned
99
89
  def expand_placeholders(content, opts={})
100
90
  if content.is_a?(Array) # Expand all array elements and go recursive
101
91
  result = []
@@ -108,7 +98,7 @@ module KingPlaceholder
108
98
  else # Only proceed with strings
109
99
  return content unless content.is_a?(String)
110
100
  end
111
- parser = KingPlaceholder::ParseContext.new(self, content, opts)
101
+ parser = KingPlaceholder::Parser.new(self, content, opts)
112
102
  parser.sm.match
113
103
  parser.result if parser.sm.state == :finished
114
104
  end
Binary file
@@ -1,25 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
- class BaseModel
4
- # include KingFormat::FormattingHelper
3
+ # Construct dummy classes
4
+ class Master
5
5
  include KingPlaceholder
6
- end
7
-
8
- # Construct dummy models
9
- class Master < BaseModel
10
6
  attr_accessor :string_field
11
7
  attr_accessor :details
12
8
  attr_accessor :side
13
9
  has_placeholders :string_field
14
10
  end
15
11
 
16
- class Side < BaseModel
12
+ class Side
13
+ include KingPlaceholder
17
14
  attr_accessor :field
18
15
  attr_accessor :master
19
16
  has_placeholders :field
20
17
  end
21
18
 
22
- class Detail < BaseModel
19
+ class Detail
20
+ include KingPlaceholder
23
21
  include KingFormat::MoneyFields
24
22
  attr_accessor :int_field, :money_field, :secret_field, :currency
25
23
  attr_accessor :master
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: king_placeholder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-27 00:00:00.000000000 Z
12
+ date: 2012-07-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: statemachine
@@ -163,14 +163,16 @@ extensions: []
163
163
  extra_rdoc_files: []
164
164
  files:
165
165
  - .gitignore
166
+ - .travis.yml
166
167
  - Gemfile
167
168
  - LICENSE
168
169
  - README.rdoc
169
170
  - Rakefile
170
171
  - king_placeholder.gemspec
171
172
  - lib/king_placeholder.rb
172
- - lib/king_placeholder/parse_context.rb
173
+ - lib/king_placeholder/parser.rb
173
174
  - lib/king_placeholder/version.rb
175
+ - placeholder_state.dia
174
176
  - spec/king_placeholder_spec.rb
175
177
  - spec/spec_helper.rb
176
178
  homepage: https://github.com/salesking/king_placeholder.git
@@ -187,7 +189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
189
  version: '0'
188
190
  segments:
189
191
  - 0
190
- hash: 2667406904980012047
192
+ hash: 2706534140845333133
191
193
  required_rubygems_version: !ruby/object:Gem::Requirement
192
194
  none: false
193
195
  requirements:
@@ -196,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
198
  version: '0'
197
199
  segments:
198
200
  - 0
199
- hash: 2667406904980012047
201
+ hash: 2706534140845333133
200
202
  requirements: []
201
203
  rubyforge_project:
202
204
  rubygems_version: 1.8.24