king_placeholder 0.0.2 → 0.0.3

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.
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